カテゴリー : CakePHP

CakePHPで、CSSとJavascriptのタグを表示する方法

CakePHPで、CSSファイル、Javascriptファイルのタグを表示する方法です。

Javascriptのタグを忘れやすいのでメモ。

まず、app/webroot/css/以下にCSSファイルを、

app/webroot/js/以下にJavascriptファイルを置きます。

Viewファイル(ここではapp/View/Layouts/default.ctp)に

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


echo $this->Html->css('import');
echo $this->Html->script('jquery');

すると以下のようにタグが表示されます。


また、複数のCSS、Javascriptを同時に指定したい場合は、

引数に配列を使用します。


echo $this->Html->css(array('import', 'jquery.bxslider'));
echo $this->Html->script(array('jquery', 'jquery.bxslider.min'));

これで以下のように複数のタグが表示されます。



CakePHPのfindでORを複数指定したい

CakePHPの検索条件でORを指定するには、以下のようにします。


$conditions = array('name IS NOT NULL',
"OR"=>array('company_id IS NULL', 'company_id'=>1)
);

これで発行されるSQLは下記のようになります。


SELECT * FROM users
WHERE
(name IS NOT NULL)
AND
((company_id IS NULL) OR (company_id = 1))

ORをANDで繋げたいという場合があります。

以下のような場合ですね。


SELECT * FROM users
WHERE
(name IS NOT NULL)
AND
((company_id IS NULL) OR (company_id = 1))
AND
((post_id IS NULL) OR (post_id = 1))

単純にORをもう一つ増やせばいいと考え、以下のようにしてみました。


$conditions = array('name IS NOT NULL',
"OR"=>array('company_id IS NULL', 'company_id'=>1),
"OR"=>array('post_id IS NULL', 'post_id'=>1)
);

しかし、上記のコードだと、以下のようになってしまいます。


SELECT * FROM users
WHERE
(name IS NOT NULL)
AND
((post_id IS NULL) OR (post_id = 1))

前のORで指定した「array(‘company_id IS NULL’, ‘company_id’=>1)」が

後ろのORで上書きされているようです。

どうすれば、と調べたところORを配列にすればよい

という記載を見つけました。


$conditions = array('name IS NOT NULL',
array("OR"=>array('company_id IS NULL', 'company_id'=>1)),
array("OR"=>array('post_id IS NULL', 'post_id'=>1))
);

こうすると、前のORが上書きされなくなりますね。

意図したように、下記のようなSQLを吐き出してくれました。


SELECT * FROM users
WHERE
(name IS NOT NULL)
AND
((company_id IS NULL) OR (company_id = 1))
AND
((post_id IS NULL) OR (post_id = 1))

以下のサイトを参考にしました。

findで複数のORを使う場合はarrayで囲む

CakePHPのfind条件にORを設定したい

CakePHPでログイン認証

CakePHPでログイン認証を実装します。
パスワードを暗号化して保存しますので、ユーザー編集画面でパスワードをデフォルトで空欄にし、入力した場合のみ変更する実装にします。

まずはユーザーテーブルを作成。

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` text,
  `username` text,
  `password` text,
  `deleted` smallint(6) DEFAULT '0',
  `created` datetime DEFAULT NULL,
  `updated` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ;

Userモデルを作成します。
app/Model/User.php
bakeで作成したものに、以下の内容を追加しました。

■パスワードの暗号化

App::uses('SimplePasswordHasher', 'Controller/Component/Auth');

AuthコンポーネントのSimplePasswordHasherを使用します。

    public function beforeSave($options = array()) {
        if (!$this->id) {
            $passwordHasher = new SimplePasswordHasher();
            $this->data['User']['password'] = $passwordHasher->hash($this->data['User']['password']);
        } else if ($this->id && $this->data['User']['password1']) {
            $passwordHasher = new SimplePasswordHasher();
            $this->data['User']['password'] = $passwordHasher->hash($this->data['User']['password1']);
		}
        return true;
    }

beforeSaveで、保存前にpasswordを暗号化します。
編集画面で、パスワード欄に何も入力しない場合は変更したくないので、変更画面(views/users/edit)のパスワードをpassword1として、これが入力されている場合に暗号化してpasswordにセットします。
登録画面では、暗号化して保存します。

■バリデーション
$validateで設定します。
パスワードは、登録画面のみ必須にしたいので、「’on’ => ‘create’」を指定します。
また、usernameは半角英数のみのユニークにしたいので、alphaNumericとisUniqueを指定します。
alphaNumericは日本語を通してしまうので、正規表現を使ってエラーチェックを設定します。

<?php
App::uses('AppModel', 'Model');
App::uses('SimplePasswordHasher', 'Controller/Component/Auth');

/**
 * User Model
 *
 */
class User extends AppModel {

    public function beforeSave($options = array()) {
        if (!$this->id) {
            $passwordHasher = new SimplePasswordHasher();
            $this->data['User']['password'] = $passwordHasher->hash($this->data['User']['password']);
        } else if ($this->id && $this->data['User']['password1']) {
            $passwordHasher = new SimplePasswordHasher();
            $this->data['User']['password'] = $passwordHasher->hash($this->data['User']['password1']);
		}
        return true;
    }

/**
 * Display field
 *
 * @var string
 */
	public $displayField = 'name';

/**
 * Validation rules
 *
 * @var array
 */
	public $validate = array(
		'name' => array(
			'notEmpty' => array(
				'rule' => array('notEmpty'),
				'message' => '必須入力です',
				//'allowEmpty' => false,
				//'required' => false,
				//'last' => false, // Stop validation after this rule
				//'on' => 'create', // Limit validation to 'create' or 'update' operations
			),
		),
		'username' => array(
			'notEmpty' => array(
				'rule' => array('notEmpty'),
				'message' => '必須入力です',
				//'allowEmpty' => false,
				//'required' => false,
				//'last' => false, // Stop validation after this rule
				//'on' => 'create', // Limit validation to 'create' or 'update' operations
			),
			'alphaNumeric' => array(
				'rule' => array('alphaNumeric'),
				'message' => '半角英数字のみ入力してください',
				//'allowEmpty' => false,
				//'required' => false,
				//'last' => false, // Stop validation after this rule
				//'on' => 'create', // Limit validation to 'create' or 'update' operations
			),
			'isUnique' => array(
				'rule' => array('isUnique'),
				'message' => 'このログインIDは既に登録されています'
			),
		),
		'password' => array(
			'notEmpty' => array(
				'rule' => array('notEmpty'),
				'message' => '必須入力です',
				//'allowEmpty' => false,
				'required' => true,
				'last' => true, // Stop validation after this rule
				'on' => 'create', // Limit validation to 'create' or 'update' operations
			),
		),
	);

	public function alphaNumeric($check) {
	    $value = array_values($check);  // 配列の添字を数値添字に変換して・・・
	    $value = $value[0];     // 最初の値を取る
	    return preg_match('/^[a-zA-Z0-9]+$/', $value);
	}
}

次にコントローラーを作成します。
app/Controller/UsersController.php

<?php
App::uses('AppController', 'Controller');
/**
 * Users Controller
 *
 * @property User $User
 * @property PaginatorComponent $Paginator
 */
class UsersController extends AppController {

/**
 * Components
 *
 * @var array
 */
	public $components = array('Paginator');

	function beforeFilter() {
		parent::beforeFilter();

		$this->Auth->allow('add', 'edit', 'logout');
	}

	function login() {
		$this->layout = 'simple';
		if ($this->request->is('post')) {
			if ($this->Auth->login()) {
				$this->redirect($this->Auth->redirect());
			} else {
				$this->Session->setFlash(__('Invalid username or password, try again'));
			}
		}
	}

	function logout() {
		$this->redirect($this->Auth->logout());
	}
	

/**
 * index method
 *
 * @return void
 */
	public function index() {
		$this->User->recursive = 0;
		$this->set('users', $this->Paginator->paginate());
	}

/**
 * view method
 *
 * @throws NotFoundException
 * @param string $id
 * @return void
 */
	public function view($id = null) {
		if (!$this->User->exists($id)) {
			throw new NotFoundException(__('Invalid user'));
		}
		$options = array('conditions' => array('User.' . $this->User->primaryKey => $id));
		$this->set('user', $this->User->find('first', $options));
	}

/**
 * add method
 *
 * @return void
 */
	public function add() {
		if ($this->request->is('post')) {
				$this->User->create();
				if ($this->User->save($this->request->data)) {
					$this->Session->setFlash(__('The user has been saved.'));
					return $this->redirect(array('action' => 'add_comp'));
				} else {
					$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
				}
		}
	}
	function add_comp(){
	}

/**
 * edit method
 *
 * @throws NotFoundException
 * @param string $id
 * @return void
 */
	public function edit($id = null) {
		if (!$id && empty($this->request->data)) {
			throw new NotFoundException(__('Invalid user'));
		}
		if ($this->request->is(array('post', 'put'))) {
				if ($this->User->save($this->request->data)) {
					$this->Session->setFlash(__('The user has been saved.'));
					return $this->redirect(array('action' => 'edit_comp'));
				} else {
					$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
				}
		} else {
			$options = array('conditions' => array('User.' . $this->User->primaryKey => $id));
			$this->request->data = $this->User->find('first', $options);
			$this->request->data['User']['password'] = null;
		}
	}
	function edit_comp(){
	}


/**
 * delete method
 *
 * @throws NotFoundException
 * @param string $id
 * @return void
 */
	public function delete($id = null) {
		$this->User->id = $id;
		if (!$this->User->exists()) {
			throw new NotFoundException(__('Invalid user'));
		}
		$this->request->onlyAllow('post', 'delete');
		if ($this->User->delete()) {
			$this->Session->setFlash(__('The user has been deleted.'));
		} else {
			$this->Session->setFlash(__('The user could not be deleted. Please, try again.'));
		}
		return $this->redirect(array('action' => 'index'));
	}
}

次に、AppController.phpで、Authコンポーネントの設定を行います。
app/Controller/AppController.php

<?php
/**
 * Application level Controller
 *
 * This file is application-wide controller file. You can put all
 * application-wide controller-related methods here.
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @package       app.Controller
 * @since         CakePHP(tm) v 0.2.9
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */

App::uses('Controller', 'Controller');

/**
 * Application Controller
 *
 * Add your application-wide methods in the class below, your controllers
 * will inherit them.
 *
 * @package		app.Controller
 * @link		http://book.cakephp.org/2.0/en/controllers.html#the-app-controller
 */
class AppController extends Controller {
    var $components = array('Session', 
            'Auth' => array(
                'authenticate' => array('Form' => array('scope' => array('User.deleted' => '0'),
														))
            ),
			);
}

最後にviewを作成します。
app/View/Users/add.ctp

<!-- app/View/Users/add.ctp -->
<div class="users form">
<?php echo $this->Form->create('User'); ?>
    <fieldset>
        <legend><?php echo __('Add User'); ?></legend>
        <?php echo $this->Form->input('username');
        echo $this->Form->input('password');
    ?>
    </fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>

ユーザー情報の編集画面です。
パスワードを「password1」にします。
app/View/Users/edit.ctp

<!-- app/View/Users/edit.ctp -->
<div class="users form">
<?php echo $this->Form->create('User', array('action'=>'edit')); ?>
    <fieldset>
        <legend><?php echo __('Add User'); ?></legend>
        <?php echo $this->Form->input('username');
        echo $this->Form->input('password1');
    ?>
    </fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>

ログイン画面です。
app/View/Users/login.ctp

<!-- app/View/Users/login.ctp -->
<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User'); ?>
    <fieldset>
        <legend><?php echo __('Please enter your username and password'); ?></legend>
        <?php echo $this->Form->input('username');
        echo $this->Form->input('password');
    ?>
    </fieldset>
<?php echo $this->Form->end(__('Login')); ?>
</div>

CakePHPでリダイレクトの際にパラメーターを設定する

CakePHPで、登録や変更処理が終わった後にリダイレクトする場合は以下のようにします。

$this->redirect(array('action' => 'index'));

これで、一覧画面にリダイレクトします。
コントローラーを指定したい場合は、以下のように指定します。

$this->redirect(array('controller' => 'orders', 'action' => 'index'));

編集画面等で、idを渡したい場合は以下のようにします。

$this->redirect(array('action' => 'edit', $id));

これで、「/コントローラー名/edit/id」というURLにリダイレクトします。

名前付きパラメーターを渡すこともできます。

$this->redirect(array('controller' => 'orders', 'action' => 'confirm', 'product' => 'pizza', 'quantity' => 5));

これで、「/orders/confirm/product:pizza/quantity:5」というURLにリダイレクトします。

ちょっとハマったのが、idをパラメーターとして渡したいときです。

$this->redirect(array('action' => 'edit', 'id' => 5));

としていたのですが、これだと「/コントローラー名/edit/id:5」というURLになってしまいます。
「’id’ => 」は不要で、

$this->redirect(array('action' => 'edit', 5));

とします。

CakePHPでレイアウトファイルを指定する

CakePHPでは、デフォルトのレイアウトファイルは
View/Layouts/default.ctp
です。

これを別のレイアウトファイルを使用する場合は、
コントローラーに以下のようにしています。

$this->layout = "sample";

同時に、
View/Layouts/sample.ctp
というファイルを作成しておきます。

これで、sample.ctpというレイアウトファイルが使用されます。

CakePHPでラジオボタンを使用する

CakePHPでラジオボタンを使用する方法です。
性別を選択するラジオボタンを表示します。

<?php
$options = array('m'=>'男性', 'f'=>'女性');
echo $this->Form->radio('sex', $options, array('legend' => false));
?>

$optionsで選択肢を配列で指定します。

CakePHPで、郵便番号から住所を自動入力したい

会員登録画面などで、郵便番号を入力すると、住所が自動入力されるというのはよく見かけます。
これをCakePHP上で実装したので、メモ。

自動入力には、ajaxzip2を使用します。
ajaxzip2のサイトから、ajaxZip2のアーカイブをダウンロードします。

解凍したajaxzip2をフォルダごとwebroot/js/におきます。
webroot/js/ajaxzip2/というフォルダ内に、
ajaxzip2.jsファイル、dataフォルダ、workフォルダが配置される形です。

viewファイルに、ajaxzip2を呼び出すように記載します。
jQueryを使用します。

<?php
echo $this->Html->script('jquery-1.9.1');
echo $this->Html->script('ajaxzip2/ajaxzip2');
?>

都道府県の選択肢を全て書いてもいいのですが、せっかくなのでDB化します。
prevesというテーブルを作成します。

CREATE TABLE IF NOT EXISTS `preves` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` text,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`)
);

データを登録します。

INSERT INTO `preves` (`id`, `name`) VALUES
(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, '沖縄県');

モデル(app/Model/Pref.php)を作成します。
クラスの定義だけです。

<?php
App::uses('AppModel', 'Model');

class Pref extends AppModel {
}
?>

コントローラーで、都道府県データを取得します。
Prefモデルを使用したいので、$usesにPrefを追加します。

var $uses = array('Pref');

コントローラー内で、以下のようにして都道府県データを取得します。

$pref_arr = $this->Pref->find('list', array('fields' => array( 'id', 'name')));
$this->set('pref_arr', $pref_arr);

Viewファイルで以下のように指定します。
Hogeはコントローラー名です。都道府県、市区町村のinputタグのnameがdata[Hoge][pref]となるので、
郵便番号の項目指定の箇所はこれに合わせます。

郵便番号: <?php echo $this->Form->input('zip', array('type'=>'text', 'div'=>false, 'label'=>false, 'size'=>"10", 'maxlength'=>"8", 'onKeyUp'=>"AjaxZip2.zip2addr(this,'data[Hoge][pref]','data[Hoge][address]',null,'strt');")); ?>
都道府県: <?php echo $this->Form->input('pref', array('type'=>'select', 'div'=>false, 'label'=>false, 'options'=>$pref_arr, 'empty'=>'選択してください')); ?>
住所: <?php echo $this->Form->input('address', array('type'=>'text', 'div'=>false, 'label'=>false, 'size'=>40)); ?>

最後に、ajaxzip2内のdataファイル呼び出しがパスの関係でうまくいっていなかったので修正。
js/ajaxzip2/ajaxzip2.jsの31行目を実際の環境にあわせて修正します(先頭にjsを追加)。

AjaxZip2.JSONDATA = 'js/ajaxzip2/data';

以上で使用できるようになると思います。

CakePHPで登録したレコードのIDを取得する

CakePHPで、登録したレコードのIDを取得する方法です。
getLastInsertID()を使用します。

$this->Model->save();
で保存した後に、以下で取得できます。

[sorcecode]
$hoge_id = $this->Model->getLastInsertID();
[/sorcecode]

CakePHPでセレクトボックスを表示する

CakePHPでセレクトボックスを表示する方法のメモ。

まず、コントローラーに選択肢の配列を指定します。
例えば、業種を選択するセレクトボックスを表示します。
コントローラー全体で使用したいので、beforeFilterで指定します。

	function beforeFilter() {
		parent::beforeFilter();

		$category_arr = array(
			"1" => "農業・林業・漁業", 
			"2" => "鉱業", 
			"3" => "建設業", 
			"4" => "製造業", 
			"5" => "電気・ガス・水道業", 
			"6" => "情報通信業", 
			"7" => "運輸業", 
			"8・小売業" => "卸売・小売業", 
			"9" => "金融・保険業", 
			"10" => "不動産業", 
			"11" => "飲食店・宿泊業", 
			"12" => "医療・福祉", 
			"13" => "教育・学習支援業", 
			"14" => "複合サービス事業", 
			"15" => "サービス業", 
			"16" => "公務"
			);
		$this->set('category_arr', $category_arr);
	}

次はViewです。

<?php echo $this->Form->input('category', array('type'=>'select', 'div'=>false, 'label'=>false, 'options'=>$category_arr, 'empty'=>'選択してください')); ?>

div、labelタグを表示したくないので、それぞれfalseを指定しています。
optionsに、コントローラーで指定した配列を設定します。
emptyは、空の選択肢を指定します。

これで、以下のようにselectタグが出力されます。

<select name="data[Hoge][category]" id="HogeCategory">
<option value="">選択してください</option>
<option value="1">農業・林業・漁業</option>
<option value="2">鉱業</option>
<option value="3">建設業</option>
<option value="4">製造業</option>
<option value="5">電気・ガス・水道業</option>
<option value="6">情報通信業</option>
<option value="7">運輸業</option>
<option value="8・小売業">卸売・小売業</option>
<option value="9">金融・保険業</option>
<option value="10">不動産業</option>
<option value="11">飲食店・宿泊業</option>
<option value="12">医療・福祉</option>
<option value="13">教育・学習支援業</option>
<option value="14">複合サービス事業</option>
<option value="15">サービス業</option>
<option value="16">公務</option>
</select>

Hogeはコントローラー名です。

CakePHPの日付入力でカレンダーを使用したい

日付項目は、データベースを作成する際に、date型で作成しますが、
これをCakePHPで実装すると、月(英語名)、日、年のプルダウンで表示されます。

日本で一般的な年、月、日の順に変更して月も英語名ではなく数字で表示させるという
カスタマイズを行うのもよいのですが、カレンダーを表示させて選択するだけという方が
入力が楽なので、これを使用しています。

以下のようなイメージです。(実装例)
カレンダー画像

カレンダーは、「protocalendar」を使用させて頂いております。
ここからダウンロードしてください。

ダウンロードしたファイルを解凍すると、以下のフォルダが生成されます。
images
javascripts
lib
sample
stylesheets

imagesフォルダを見ると、metal、paper、simpleフォルダがあります。
3つのデザインから好きなものを選んでください。
stylesheetsにも同じ名前のCSSファイルがありますので、使用するものをコピーしてwebroot/css/以下に配置します。

javascriptsフォルダには、protocalendar.js本体と各言語ごとのjsファイルがありますので、
使用する言語のjsファイル(日本語だとlang_ja.js)をwebroot/js/以下に配置します。
他に、libフォルダにeffects.jsとprototype.jsがありますので、同様にwebroot/js/以下に配置します。
(すでに存在する場合は不要です)

ファイルを配置したら、これらを呼び出します。
views/layouts/default.ctpあたりに以下を追加します。

<?php
echo $html->css('simple'); 
echo $javascript->link('prototype'); 
echo $javascript->link('effects'); 
echo $javascript->link('protocalendar'); 
echo $javascript->link('lang_ja');  
?>

viewファイルには、以下のように書きます。
add.ctp

<?php echo $form->input('hoo_day',array('type'=>'text','size'=>"10")); ?>

‘type’=>’text’とするのがミソです。

あと、同じviewファイル内にprotocalendarの宣言を書いてやります。
add.ctp

<script type="text/javascript">  
InputCalendar.createOnLoaded('HogeHooDay', {format: 'yyyy-mm-dd', lang:'ja'});
</script>

HogeHooDayは、inputタグのidになります。
CakePHPでは、idはコントローラー名+項目名になりますので、上記の例ですとHogeHooDayになります。

「Hoge」はコントローラー名で、フォームの宣言部分に出てきます。

<?php echo $form->create('Hoge');?>

このコントローラー名に項目名をキャメル記法でつなげます。

format部分は、日付形式を指定します。「yyyy-mm-dd」としておけば、このままMySQLのdate型に放り込めるので便利です。
コントローラー側でデフォルト値を今日とかに指定しておけば、カレンダーで選択済みの状態になります。
lang部分は、日本語のjaを指定してください。

以上で、カーソルが入った時点でカレンダーが表示されます。
日付をクリックするだけで日付入力ができるので便利ですよ!