■目的
CakePHPでWebシステムを構築中です。
あるデータテーブルAの検索フォームにて、マスタテーブルBのnameフィールドに入っているデータをオートコンプリートで補完入力したいです。
■開発環境
CakePHP 2.7.3
PHP 5.6.8
jQuery 1.11.3
jQuery UI 1.11.4
■参考にしたサイト
http://junichi11.com/?p=423
http://www.buildinsider.net/web/jqueryuiref/0019
http://js.studio-kingdom.com/jqueryui/widgets/autocomplete
■実装手順
-
jqueryとjquery UIをレイアウトファイルで指定。
-
テーブルAのコントローラに、オートコンプリートのデータを取得するメソッドを追加。
TableAController.php
php
1public function autocomplete(){ 2 $this->loadModel('TableB'); 3 $field = 'name'; 4 $term = $this->params['url']['term']; 5 // 入力値 無:全てのデータの中から10件返す。 6 // 入力値 有:入力値を含むデータを10件返す 7 $condition = array(); 8 if(!empty($term)){ 9 $condition = array('TableB.'.$field.' like' => "%".$term."%"); 10 } 11 12 $query = array( 13 'fields' => array($field), 14 'conditions' => $condition, 15 'limit' => 10, 16 'order' => array('TableB.'.$field => 'ASC'), 17 'group' => $field, 18 ); 19 20 $data = array(); 21 $items = $this->TableB->find('all', $query); 22 23 foreach ($items as $item) { 24 $cnt = array_push($data, $item['TableB'][$field]); 25 } 26 27 // JSON形式の文字列を生成 28 $json = json_encode($data); 29 echo $json; 30} 31
この時点でautocomplete()メソッドの動作確認をしました。
このURLにアクセスすると、テーブルBからnameフィールドに"hoge"が含まれるレコードがJSON形式で返ってくることが確認できています。
- テーブルAのビューにjavascriptとテキストボックスを作成。
TableA/index.ctp
php
1<script type="text/javascript"> 2$(function(){ 3 $('#autocomplete').autocomplete({ 4 source: '/TableA/autocomplete', 5 autoFocus: true, 6 delay: 500, 7 minLength: 2 8 }); 9}) 10</script> 11 12<?php echo $this->Form->input('name', array( 13 'type' => 'text', 14 'id' => 'autocomplete', 15)); ?>
これでテキストボックスが作成されますが、入力してもオートコンプリートが動作しません。
2.の時点でautocomplete()メソッド自体は正常に動作している(と思われる)ので、怪しいのはjavascriptかと思っています。
特にsource: '/TableA/autocomplete',の部分が、ちゃんとしたURLを参照できているのかが疑わしいです。
しかし実際のところどういう動作をしているのか確認する方法が分からず、詰まっています。
2015/10/29追記
ipadcaronさんのアドバイスを元にシンプルな構成で検証してみました。
CakePHPでの動作は一旦忘れて、htmlとphpだけで構成してみました。
json_echo.php
php
1<?php 2 echo '[{"id":"1","value":"item1"},{"id":"2","value":"item2"},{"id":"3","value":"item3"}]'; 3?>
index.html
html
1<!doctype html> 2<html lang="en"> 3<head> 4 <meta charset="utf-8"> 5 <title>jQuery UI Autocomplete - Remote datasource</title> 6 <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css"> 7 <script src="//code.jquery.com/jquery-1.10.2.js"></script> 8 <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script> 9 <script> 10 $(function() { 11 function log( message ) { 12 $( "<div>" ).text( message ).prependTo( "#log" ); 13 $( "#log" ).scrollTop( 0 ); 14 } 15 16 $( "#birds" ).autocomplete({ 17 source: "json_echo.php", 18 minLength: 2, 19 select: function( event, ui ) { 20 log( ui.item ? 21 "Selected: " + ui.item.value + " aka " + ui.item.id : 22 "Nothing selected, input was " + this.value ); 23 } 24 }); 25 }); 26 </script> 27 </head> 28<body> 29 30<div class="ui-widget"> 31 <label for="birds">Birds: </label> 32 <input id="birds"> 33</div> 34 35<div class="ui-widget" style="margin-top:2em; font-family:Arial"> 36 Result: 37 <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div> 38</div> 39 40 41</body> 42</html>
index.htmlを開きテキストボックスに入力してみたところ、item13が候補として表示されることを確認しました。3が表示されるということで、正しい動作だと思います。
この場合はどんな文字列を入力しても、echoで返されるJSONデータは決まっているので常にitem1
この結果を受けてCakePHPで以下のように実装しました。
TableAController.php
php
1 public function autocomplete(){ 2 echo '[{"id":"1","value":"item1"},{"id":"2","value":"item2"},{"id":"3","value":"item3"}]'; 3 } 4
TableA/index.ctp
html
1 <script> 2 $(function() { 3 function log( message ) { 4 $( "<div>" ).text( message ).prependTo( "#log" ); 5 $( "#log" ).scrollTop( 0 ); 6 } 7 8 $( "#birds" ).autocomplete({ 9 source: '<?php echo $this->Html->url(array('controller' => 'OrderLedgers', 'action' => 'autocomplete')); ?>', 10 minLength: 2, 11 select: function( event, ui ) { 12 log( ui.item ? 13 "Selected: " + ui.item.value + " aka " + ui.item.id : 14 "Nothing selected, input was " + this.value ); 15 } 16 }); 17 }); 18 </script> 19 20HTML部分は同一
これを実行してみましたが、動作せず。。。
そしてたった今、Chromeのブラウザコンソールに**500 (Internal Server Error)**が表示されていることに気付きました。
(途中でeripongさん、tozjpさんが言っていたのはこういう所を見ろということだったんですね・・・)
少し前進した気がしますので、もう少し調べてみたいと思います。
解決しました!
根本的な原因は、tozjpさんのご指摘の通り、autocompleteメソッドからのレスポンスが純粋なJSONではなかったことでした。
CakePHPがビューなどを自動で付与したデータを返していたため、javascript側で正常に処理できなかったのだと思われます。
http://digape.com/201210/cakephp2-json%E5%BD%A2%E5%BC%8F%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%89%8B%E8%BB%BD%E3%81%AB%E5%87%BA%E5%8A%9B%E3%81%99%E3%82%8B/
http://www.sssg.org/blogs/hiro345/archives/14793.html
上記のリンクを参考に、JSONのみを返すアクションに修正しました。
まずは予め用意した配列を返せるか検証。
TableAController.php
php
1 public function autocomplete(){ 2 $result = array( 3 ['id' => '1', 'value' => 'item1'], 4 ['id' => '2', 'value' => 'item2'], 5 ['id' => '3', 'value' => 'item3'], 6 ); 7 $this->viewClass = 'Json'; 8 $this->set(compact('result')); 9 $this->set('_serialize', 'result'); 10 }
autocompleteアクションを単体で実行し、JSONのみが表示されることを確認しました。
テキストボックスに入力してみると、item1~3の候補がドロップダウンされることも確認。
続いて元々やりたかった別テーブルからのデータを取得するように修正。
TableAController.php
php
1public function autocomplete(){ 2 $this->loadModel('TableB'); 3 $field = 'name'; 4 $term = $this->params['url']['term']; 5 6 // 入力値 無:全てのデータの中から10件返す。 7 // 入力値 有:入力値を含むデータを10件返す 8 $condition = array(); 9 if(!empty($term)){ 10 $condition = array('TableB.'.$field.' like' => "%".$term."%"); 11 } 12 13 $query = array( 14 'fields' => array($field), 15 'conditions' => $condition, 16 'limit' => 10, 17 'order' => array('TableB.'.$field => 'ASC'), 18 'group' => $field, 19 ); 20 21 $data = array(); 22 $items = $this->TableB->find('all', $query); 23 24 foreach ($items as $item) { 25 $cnt = array_push($data, $item['TableB'][$field]); 26 } 27 28 // JSONデータのみを返す 29 $this->viewClass = 'Json'; 30 $this->set(compact('data')); 31 $this->set('_serialize', 'data'); 32}
ビュー側はこうです。
TableA/index.ctp
php
1<script type="text/javascript"> 2$(function(){ 3 $('#autocomplete').autocomplete({ 4 source: '<?php echo $this->Html->url(array('controller' => 'TableA', 'action' => 'autocomplete')); ?>', 5 autoFocus: true, 6 delay: 500, 7 minLength: 2 8 }); 9}) 10</script> 11 12<?php echo $this->Form->input('name', array( 13 'type' => 'text', 14 'id' => 'autocomplete', 15)); ?>
これにより、ちゃんとテーブルBのデータがオートコンプリート表示されるようになりました。

回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/10/28 16:24
2015/10/28 16:36