Cakephp2 独自SQLでPaginateしたやつのソートと検索。
これもはまった。
独自SQLでPaginateするのはググると結構出てくるので割愛。
ソートにはまった。はまったはまった。
結論:「virtualFields」を使う。
あ…割愛しないでやっぱり書きます。えぇ、将来の自分のためにですとも!!
PaginateOrigin.php(この子があることで独自SQLでのPaginateが可能になります)
App::uses('AppModel', 'Model'); class PaginateOrigin extends AppModel { public $name = 'PaginateOrigin'; public $useTable = false; public $virtualFields = array( 'teh_experience_id' => 'PaginateOrigin.experience_id', 'teh_nickname' => 'PaginateOrigin.nickname', 'teh_edit_time' => 'PaginateOrigin.edit_time', ); /** * ページネート実行 未審査取得 * */ public function paginate() { $condition = func_get_arg(0); $fileds = func_get_arg(1); $order = func_get_arg(2); $limit = func_get_arg(3); $page = func_get_arg(4); $recursive = func_get_arg(5); $extra = func_get_arg(6); // SQL文 $sql = $extra['extra']['type']; if (count($order) > 0) { $strOrderSql = ''; $numCnt = 0; foreach ($order as $key => $value) { $keys = explode('.', $key); $key = $keys[1]; if ($numCnt > 0) { $strOrderSql .= ',' . $key . ' ' . $value; } else { $strOrderSql .= $key . ' ' . $value; } $numCnt++; } $sql .= ' ORDER BY ' . $strOrderSql; } $sql .= ' LIMIT ' . $limit; if ($page > 1) { $sql .= ' OFFSET ' . ($limit * ($page - 1)); } return $this->query($sql); } /** * ページネート実行(カウント処理) * */ public function paginateCount() { $extra = func_get_arg(2); return count($this->query( preg_replace( '/LIMIT \d+ OFFSET \d+$/u', '', $extra['extra']['type'] ) )); } public function hasField($name, $checkVirtual = false) { return true; } }
ArticleExamination.phpという独自SQLを書いたもの
App::uses('PaginateOrigin', 'Model'); class ArticleExamination extends PaginateOrigin { public $name = 'ArticleExamination'; public $useTable = false; public function getList($in, $where) { $in = implode(',', $in); $strSql = <<< EOM SELECT teh.post_status AS post_status ,teh.working_on_flg AS working_on_flg ,teh.experience_id AS experience_id ,m_group.group_name AS group_name ,teh.post_title AS post_title ,user_info.nickname AS nickname ,teh.edit_time AS edit_time ,teh.plan_team_judgment_status AS plan_team_judgment_status ,plan_account.name AS plan_account_name ,teh.editor_team_judgment_status AS editor_team_judgment_status ,editor_account.name AS editor_account_name FROM t_experience_history AS teh LEFT OUTER JOIN m_group ON teh.post_group_id = m_group.id LEFT OUTER JOIN ( SELECT t_user.nickname AS nickname ,t_experience.id AS experience_id FROM t_user INNER JOIN t_experience ON t_user.id = t_experience.user_id ) AS user_info ON teh.experience_id = user_info.experience_id LEFT OUTER JOIN m_management_account AS plan_account ON teh.plan_team_judgment_account_id = plan_account.id LEFT OUTER JOIN m_management_account AS editor_account ON teh.editor_team_judgment_account_id = editor_account.id WHERE teh.post_status IN ({$in}) AND teh.delete_flg = 0 EOM; if ($where) { $strWhere = <<< EOM AND ( user_info.nickname LIKE '%{$where}%' OR teh.post_title LIKE '%{$where}%' OR teh.experience_id LIKE '%{$where}%' ) EOM; $strSql .= $strWhere; } return $strSql; } }
これで準備OK
さて本題のController
App::uses('AppController', 'Controller'); class ArticleExaminationController extends AppController { // レイアウト指定 public $layout = 'adminMainLayout'; public $uses = array('PaginateOrigin', 'ArticleExamination'); public $components = array('Admin', 'Paginator'); public function entryList() { $user = $this->Auth->user(); if (is_null($user)) { $this->redirect('/admin/'); } // 検索データがある場合、Where句に設定 $where = null; if ($this->data['search']) { $where = $this->data['search']; } // データ取得条件設定 switch ($this->request->params['poststatus']) { case 'examination': // 未審査・審査中 $in = array(TExperienceHistory::UNEXAMINED, TExperienceHistory::UNDER_EXAMINATION); break; case 'judgement': // 編集長判断待ち $in = array(TExperienceHistory::CHIEF_JUDGEMENT_WAITING); break; case 'reject': // 差し戻し $in = array(TExperienceHistory::REMAND_WAITING); break; case 'waiting': // 公開待ち $in = array(TExperienceHistory::PUBLIC_WAITING); break; case 'non_examination': // 公開済・差し戻し済 $in = array(TExperienceHistory::PUBLISHED, TExperienceHistory::REMAND_ALREADY); break; default: $this->redirect('/admin/dashboard/'); break; } // SQL $query = array( 'order' => array('teh.id' => 'asc'), 'limit' => 20, 'extra' => array( 'type' => $this->ArticleExamination->getList($in, $where), ), ); // ページャー設定 $this->Paginator->settings = $query; // データ取得 $data = $this->Paginator->paginate('PaginateOrigin'); // リストデータ $this->set('data', $data); // View設定 $this->render('/Admin/ArticleExamination/entry_list'); } この辺は関係ないので割愛 }
あ、あとViewね。これ大事
echo $this->Html->url(array('controller' => 'ArticleExamination', 'action' => 'entryList', 'poststatus' => $this->params['poststatus'])) . '/page:1/sort:PaginateOrigin.nickname/direction:asc';
こんな感じです
重要なのは、PaginateOrigin.phpの
public $virtualFields = array( 'teh_experience_id' => 'PaginateOrigin.experience_id', 'teh_nickname' => 'PaginateOrigin.nickname', 'teh_edit_time' => 'PaginateOrigin.edit_time', );
ですな。ソートで使いたいやつを宣言しておきます。
'一意になればなんでもいい' => 'モデル名.カラム名'
ってなってます。
コントローラのポイントは
$uses = array('PaginateOrigin', 'ArticleExamination');
です。PaginateOrigin($virtualFieldsを定義したModel)を一番左に書かないとソートうまく動かないんです。めんどくさい。
本当にざっとですみません。
Cakephpの認証時に独自テーブルを使ってはまりました。
えぇ、はまりましたよ。
Auth認証。
本家ではUserテーブルを使ってますよね。
認証 — CakePHP Cookbook 2.x ドキュメント
今回はUserテーブル使いません。usernameなんてフィールドもありません。
認証に使うテーブル:ManagementAccount
各フィールド名
username→login_id
password→そのまま
さて。
まずModel
<?php App::uses('AppModel', 'Model'); App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth'); App::uses('AuthComponent', 'Controller/Component'); class ManagementAccount extends AppModel { public $name = 'ManagementAccount'; public $useTable = 'm_management_account'; public $validate = array( 'login_id' => array( 'required' => array( 'rule' => 'notBlank', 'message' => 'A username is required' ) ), 'password' => array( 'required' => array( 'rule' => 'notBlank', 'message' => 'A password is required' ) ), ); public function beforeSave($options = array()) { if (isset($this->data[$this->alias]['password'])) { $passwordHasher = new BlowfishPasswordHasher(); $this->data[$this->alias]['password'] = $passwordHasher->hash( $this->data[$this->alias]['password'] ); } return true; } }
ここでポイントになるのが「AuthComponent」こいつを使う宣言をすること
次にController
App::uses('AppController', 'Controller'); class AdminLoginController extends AppController { public $components = array('Auth'); // レイアウト指定 public $layout = 'adminLoginLayout'; public function beforeFilter(){ $this->autoLayout = false; parent::beforeFilter(); $this->Auth->autoRedirect = false; $this->Auth->allow('index', 'logout'); $this->Auth->loginAction = '/admin/login/'; $this->Auth->loginRedirect = '/admin/dashboard'; $this->Auth->logoutRedirect = '/admin/'; $this->Auth->authorize = 'Controller'; // フォームの認証設定 $this->Auth->authenticate = array( // フォーム認証を利用 'Form' => array( // 認証に利用するモデルの変更 'userModel' => 'ManagementAccount', //HogeUserモデルを指定 // 認証に利用するモデルのフィードを変更 'fields' => array('username' => 'login_id', 'password' => 'password') ) ); } public function index() { $this->set('title_for_layout', '管理画面'); } public function login() { if ($this->request->is('post')) { if ($this->Auth->login()) { $this->redirect('/admin/dashboard'); } else { //$this->Session->setFlash(__('Invalid username or password, try again')); $this->redirect('/admin/'); } } else { $this->redirect('/admin/'); } } public function dashboard() { } public function logout() { $this->redirect($this->Auth->logout()); } }
ここでのポイントは「$this->Auth->authenticate 」の設定
次にView
<?php echo $this->Form->create('ManagementAccount', array('url' => '/admin/login')); ?>
<table>
<tbody>
<tr>
<th>
<img src="/img/admin/icon_user.gif" width="16" height="14" alt="ユーザーID">
</th>
<td>
<input name="data[ManagementAccount][login_id]" type="text" id="ManagementAccountUsername"/>
</td>
</tr>
<tr>
<th>
<img src="/img/admin/icon_pass.gif" width="16" height="17" alt="パスワード">
</th>
<td>
<?php echo $this->Form->input('password', array('label' => false)); ?>
</td>
</tr>
</tbody>
</table>
<p class="login">
</p>
<?php echo $this->Form->end('ログイン'); ?>
login_idだけ、Formヘルパーで生成するとセレクトボックスになってしまうので手書きです
これだけなんだけどね!
あとあと、重要なんだけど、パスワードはvar_dumpとかでAuthComponent::password使ってハッシュ化したものをテーブルに登録しておかないと通らないよ!
ajaxの処理が完了してから実行したい処理がある。
ajaxってさ、非同期通信で便利なんだけど、処理結果によって後続の処理をどうするか決めたい時あるじゃん?
ajax実行(登録最大値を取得している)
↓
処理A
って書いても、ajaxの処理中に処理Aが実行されて、登録最大値越してるのに!なんか処理A走ってる!てきなことになりました。
結論からいうと、$.when $.doneってのを使いました
$.when( $.ajax({ url: '/check_max_user', type: 'POST', dataType: 'json', success: function(data, status, xhr) { if (!data.success) { max_user_flg = true; alert('お腹いっぱいだお'); return false; } else { max_user_flg = false; } } }) ).done(function(){ if (!max_user_flg) { 以下略
こうするとね、whenの中身実行してからdoneの処理を実行するわけですよ。
便利だな。
気が付いたんだけどさ、最近phpネタ書いてない…orz
jQuery File Uploadで任意のタイミングであぷろーどさせたい。
環境は一式そろってて、改修が必要だった。
現行:ファイル選択後そのままアップロード処理実行
改修内容:「確認」ボタンを押してからアップロード処理実行
addってところで、確認ボタンが押されてからsubmitするよってことになります。
それだけ。
$('#csvupload').fileupload({ dataType: 'json', url: $.seap.webroot + 'user_account/csv/import', add: function(e,data) { data.context = $('.button_edit').click(function(){ data.submit(); }); }, done: function (e, data) { if(data.result.length > 0) { var result = true; var done_msg = ''; var fail_msg = ''; for(var fi = 0; fi < data.result.length; fi++) { result = result && data.result[fi]['success']; if(data.result[fi]['success'] == true) { done_msg += "\n" + data.result[fi]['message']; } else { fail_msg += "\n" + data.result[fi]['message']; } } if(result) { alert("アップロード完了" + done_msg); location.href = $.seap.webroot + 'user_account/'; } else { alert("一括登録に失敗しました。" + fail_msg.replace('\\n','\n')); $('.filename').text(''); } $('.csv_drop_field .label').html(''); } }, fail: function (e, data) { alert("アップロードに失敗しました"); $('.csv_drop_field .label').html(''); }, progressall: function (e, data) { var progress = parseInt(data.loaded / data.total * 100, 10); var progresshtml = "アップロード中 : " + progress + "%"; $('.csv_drop_field .label').html(progresshtml); }, }); 以下略。
jQuery Uniform ajax
いい題名が思いつかなかったw
さて。
ページ遷移時、ajaxから結果取得してjsでHTML生成して吐き出してるのですが、その際にuniformをあてても反映されない。どうしたものか。
結果↓
$( "button").uniform();
$.uniform.update();
今回は、buttonだけ反映させたかったのでbuttonだけ。
他にも適用させたかったら
$( "select, input:checkbox, input:radio, input:file").uniform();
$.uniform.update();
みたいに指定すればいいらしい。