i2i無料WEBパーツ
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
Flex+S2Dao.PHPを使おうとなると、Zend_Amfじゃあなくて、AMFPHPのほうが便利かな?と思いAMFPHPの勉強がてら、DataGridのページングを実装してみました。
これが、また苦労の連続でしたが・・・(つ_・。)
まあ、Flexもあまり詳しくないし、PHPも初心者なんでしょうがないといえばしょうがないのですが。

以下、備忘録です。

■やりたいこと
Flex+PHPでページング処理を実行したいワケ。

■環境
・Flex 3.2
・PHP 5.2.9
・MySQL 5.1
・S2Container.PHP 1.1.2
・S2Dao.PHP 1.1.2
・S2Pager.PHP

■プログラムの構成
<Flex>
・BlogListAmfphp.xml
 →BLOGテーブルの一覧を取得する画面。
・DataGridPagingControl.as
 →DataGridをページングする為のコントロール。
・CustomEvent.as
 →DataGridPagingControl.asのイベント発生時に、パラメータを設定できるようにしたEventの拡張クラス。
・main.css
 →おもにDataGridPagingControl.asを装飾するCSSファイル

<PHP>
・BlogService.PHP
 →BLOGテーブルにアクセスし、データを取得する。一覧データとページング情報をクライアントに返します。
・BlogPagerCondition.php
 →S2Dao_DefaultPagerConditionを継承した検索条件です。検索条件ってほぼEntityと同じなんだけど、多重継承できないからEntityを継承できないわけで・・・。
  まあ、大体はEntityをコピペして作成すんのかねぇ・・・。
・S2Dao_PagerSupportCopy.php
 →S2Dao_PagerSupport.phpを勝手にコピーして修正したクラス。セッション情報から取得したオブジェクトが__PHP_Incomplete_Classとならないように修正した(つもり)。
  拡張して使えばいいんじゃね?って話もあるが、プロパティがPrivateなんでコピーしてみた(汗

■画面
・初期表示
00初期画面
・検索後
01検索後
・ページング
02.jpg

■感想
 検索中のボタン表示制御とか色々つくり込まなければならないところはあるけれど、サンプルとしてはこんな感じかしら?
 ページコントロールは、ラベルじゃなくてボタンにすればよかったかなぁ・・・。Flex側のパッケージも適当すぎるし・・・orz。

・まず、AMFPHPもZend_Amfと同じような使いかたかな。PHP側の処理がまずくて下記のようなエラーがよくでます。対応としては、PHP側の処理が正しいかとにかくログを出力して検証するですね。
Error #2044: ハンドルされていない NetStatusEvent : level=error, code=NetConnection.Call.BadVersion
at BlogListAmfphp/getCon()[C:\project\flextest\BlogListAmfphp.mxml:64]
at BlogListAmfphp/search()[C:\project\flextest\BlogListAmfphp.mxml:40]
at BlogListAmfphp/__iconButton_click()[C:\project\flextest\BlogListAmfphp.mxml:112]


・次に、S2Pager.PHPに嵌まるわけ。テストクラスから実行した時には問題ないんだけど、Flexからサービスを呼び出すとこけるとか。。。
 その対応で、S2Dao_PagerSupportを修正して使ったんだけど、正しい対応かどうかは・・・自信なし!(ぇ
 詳細は明日以降の記事に書くとしますね。

嵌まりポイントは多々ありましたが、なんとかページングできました。S2Pager.PHP、いい感じですね^^

■参考サイト

PHPPRO AMFPHPとは? PHPとFlexの連携 (その1)

amfphp

amfphp download

S2Dao_PagerSupport - セッションへの検索条件の格納をサポート

ソースは長いので続きの方に記述してあります。。。
■ソース
・テーブル定義

CREATE TABLE BLOG(
BLOG_ID INT AUTO_INCREMENT COMMENT 'ブログ番号'
,TITLE VARCHAR(512) COMMENT 'タイトル'
,URL VARCHAR(1024) COMMENT 'URL'
,UPDATE_DATE DATETIME COMMENT '更新日時'
,PRIMARY KEY (BLOG_ID)
)
/


・BlogListAmfphp.xml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" xmlns:fw.control="src.control.*"
initialize="init()"
>

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;
import flash.net.Responder;
import mx.controls.Alert;
import flash.events.Event;

import src.control.CustomEvent;

// ゲートウェイスクリプトのパス
private const GATEWAY_URL:String = 'http://localhost/php-example/service/amGateway.php';

[Bindable]
private var blogListArray:Array = [];

/** DataGridに表示する行数 */
private var pageLimit:Number = 5;

/**
* 初期処理
*/
public function init():void {

//データグリッドページングコントロールからのイベントをハンドリングする
dgpc.addEventListener("EVENT_PAGING", paging);
}

/**
* 検索を実行
*/
private function search():void {

reset();

var responder:Responder = new Responder(onResult, onFault);
var title:String = ti_blogName.text;
var con:NetConnection = getCon();
con.call('BlogService.selectByCondition', responder, title, 0, pageLimit);
}

/**
* ページングを実行
*/
private function paging(event:CustomEvent):void {

reset();

var responder:Responder = new Responder(onResult, onFault);
var title:String = ti_blogName.text;
var offset:String = event.getParam("OFFSET");
var con:NetConnection = getCon();
con.call('BlogService.selectByCondition', responder, title, offset , pageLimit);
}

/**
* AMFコネクション初期化
*/
private function getCon():NetConnection {
var time:Number = (new Date()).getTime();
var con:NetConnection = new NetConnection();
con.connect(GATEWAY_URL + '?time=' + time);
con.objectEncoding = ObjectEncoding.AMF3;
return con;
}

/**
* 成功
*/
private function onResult(ret:*):void {
if (typeof ret == 'string') {
//エラーメッセージを表示
Alert.show(ret);
} else {
//一覧を設定
blogListArray = ret.RESULT;
//ページング情報をページングコントロールにセット
dgpc.setResult(ret.OFFSET_INFO);
}
}

/**
* 失敗
*/
private function onFault(etc:*):void {
Alert.show('通信に失敗しました');
}

/**
* 表示を初期化します。
*/
private function reset():void {
blogListArray = [];
dgpc.reset();
}

]]>
</mx:Script>
<mx:Style source="css/main.css" />

<mx:Panel title="ブログ一覧"
layout="horizontal"
height="100%" width="100%"
paddingTop="10" paddingBottom="10"
paddingLeft="10" paddingRight="10" >
<mx:VBox width="100%">
<mx:Button id="iconButton" label="検索"
paddingLeft="12" paddingRight="18" labelPlacement="right"
color="#993300" click="search()"/>
<mx:HBox width="100%">
<mx:Text width="7%" color="blue" text="ブログ名" />
<mx:TextInput id="ti_blogName" width="43%" color="blue"/>
<mx:Spacer width="50%" />
</mx:HBox>
<fw.control:DataGridPagingControl id="dgpc" />
<mx:DataGrid id="dg_blogList" dataProvider="{blogListArray}" width="100%">
<mx:columns>
<mx:DataGridColumn headerText="BLOG_ID" dataField="blogId" width="80"/>
<mx:DataGridColumn headerText="タイトル" dataField="title" width="140"/>
<mx:DataGridColumn headerText="URL" dataField="url"/>
<mx:DataGridColumn headerText="更新日時" dataField="updateDate"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:Panel>
</mx:Application>


・DataGridPagingControl.as

package src.control
{
import flash.events.MouseEvent;
import mx.containers.HBox;
import mx.controls.Label;

public class DataGridPagingControl extends HBox
{

/** 1ページに表示できるレコード数 */
private var limit:Number;
/** 全レコード数 */
private var count:Number;
/** 現在のオフセット */
private var offset:Number;
/** <ボタンがクリック可能の場合はtrue */
private var isNext:Boolean;
/** >ボタンがクリック可能の場合はtrue */
private var isPrev:Boolean;
/** >ボタンを押したときに設定するオフセット */
private var nextOffset:Number;
/** <ボタンを押したときに設定するオフセット */
private var prevOffset:Number;
/** 現在のページインデックス */
private var pageIndex:Number;
/** 最後のページインデックス */
private var lastPageIndex:Number;
/** 部品を格納するボックス */
private var hb_pagingLabel:HBox = new HBox();
/** <<のラベル */
private var firstLabel:Label = new Label();
/** <のラベル */
private var prevLabel:Label = new Label();
/** >のラベル */
private var nextLabel:Label = new Label();
/** >>のラベル */
private var lastLabel:Label = new Label();
/** ページ情報表示ラベル */
private var lb_count:Label = new Label();

/**
* 各コントロールを生成する
*/
protected override function createChildren():void {

firstLabel.styleName = "labelPageFirst";
firstLabel.text = "<<";
prevLabel.styleName = "labelPagePrev";
prevLabel.text = "<";
nextLabel.styleName = "labelPageNext";
nextLabel.text =">";
lastLabel.styleName = "labelPageLast";
lastLabel.text =">>";
hb_pagingLabel.setStyle("horizontalGap",0);
lb_count.percentWidth = 100;

firstLabel.addEventListener(MouseEvent.CLICK ,firstLabelClickHandler);
prevLabel.addEventListener(MouseEvent.CLICK ,prevLabelClickHandler);
nextLabel.addEventListener(MouseEvent.CLICK ,nextLabelClickHandler);
lastLabel.addEventListener(MouseEvent.CLICK ,lastLabelClickHandler);

firstLabel.addEventListener(MouseEvent.MOUSE_OVER, firstLabelMouseOverHandler);
prevLabel.addEventListener(MouseEvent.MOUSE_OVER, prevLabelMouseOverHandler);
nextLabel.addEventListener(MouseEvent.MOUSE_OVER, nextLabelMouseOverHandler);
lastLabel.addEventListener(MouseEvent.MOUSE_OVER, lastLabelMouseOverHandler);

firstLabel.addEventListener(MouseEvent.MOUSE_OUT, firstLabelMouseOutHandler);
prevLabel.addEventListener(MouseEvent.MOUSE_OUT, prevLabelMouseOutHandler);
nextLabel.addEventListener(MouseEvent.MOUSE_OUT, nextLabelMouseOutHandler);
lastLabel.addEventListener(MouseEvent.MOUSE_OUT, lastLabelMouseOutHandler);

hb_pagingLabel.addChild(firstLabel);
hb_pagingLabel.addChild(prevLabel);
hb_pagingLabel.addChild(nextLabel);
hb_pagingLabel.addChild(lastLabel);

this.addChild(hb_pagingLabel);
this.addChild(lb_count);

this.reset();

super.createChildren();
}

/**
* ページングのコントロールの表示を設定
*/
private function setPagingVisible():void {

//データが無い場合は、ページング処理をしない
if( count < 1 ) return;
// 戻るページがあれば<と<<を有効にする
if( isPrev > 0 ){
firstLabel.enabled = true;
prevLabel.enabled = true;
}
// 次のページがあれば>と>>を有効にする
if( isNext ){
nextLabel.enabled = true;
lastLabel.enabled = true;
}
// カウントテキストを設定する
lb_count.text = ( pageIndex + 1 ) + "ページ / 全" + ( lastPageIndex + 1 ) +
"ページ (全" + count +" レコード)";
}

/**
* ページコントロールボタンとデータ件数表示をリセットする
*/
public function reset():void {

firstLabel.enabled = false;
prevLabel.enabled = false;
nextLabel.enabled = false;
lastLabel.enabled = false;
lb_count.text = "";
}

/**
* ページング情報を設定します。
*/
public function setResult(result:Object):void {
reset();
if(result != null){

limit = result.LIMIT;
count = result.COUNT;
offset = result.OFFSET;
isPrev = Boolean(result.IS_PREV);
isNext = Boolean(result.IS_NEXT);
nextOffset = result.NEXT_OFFSET;
prevOffset = result.PREV_OFFSET;
pageIndex = result.PAGE_INDEX;
lastPageIndex = result.LAST_PAGE_INDEX;
setPagingVisible();
}
}

/**
* clickイベントに対応したハンドラ
*/
private function firstLabelClickHandler(event:MouseEvent):void {
setRequestPage(0);
}
private function prevLabelClickHandler(event:MouseEvent):void {
setRequestPage(1);
}
private function nextLabelClickHandler(event:MouseEvent):void {
setRequestPage(2);
}
private function lastLabelClickHandler(event:MouseEvent):void {
setRequestPage(3);
}

/**
* 各ボタンのMouseOverイベントハンドラ
*/
private function firstLabelMouseOverHandler(event:MouseEvent):void {
firstLabel.styleName = "labelPageFirst" + "MouseOver";
}
private function prevLabelMouseOverHandler(event:MouseEvent):void {
prevLabel.styleName = "labelPagePrev" + "MouseOver";
}
private function nextLabelMouseOverHandler(event:MouseEvent):void {
nextLabel.styleName = "labelPageNext" + "MouseOver";
}
private function lastLabelMouseOverHandler(event:MouseEvent):void {
lastLabel.styleName = "labelPageLast" + "MouseOver";
}

/**
* 各ボタンのMouseOutイベントハンドラ
*/
private function firstLabelMouseOutHandler(event:MouseEvent):void {
firstLabel.styleName = "labelPageFirst" + "MouseOut";
}
private function prevLabelMouseOutHandler(event:MouseEvent):void {
prevLabel.styleName = "labelPagePrev" + "MouseOut";
}
private function nextLabelMouseOutHandler(event:MouseEvent):void {
nextLabel.styleName = "labelPageNext" + "MouseOut";
}
private function lastLabelMouseOutHandler(event:MouseEvent):void {
lastLabel.styleName = "labelPageLast" + "MouseOut";
}

private function setRequestPage(action:Number):void {

switch(action) {

// <<
case 0:
offset = 0;
break;

// <
case 1:
offset = prevOffset;
if( offset < 0 ) offset = 0;
break;

// >
case 2:
offset = nextOffset;
if( offset > count ) offset = count - limit;
break;

// >>
case 3:
offset = lastPageIndex * limit;
break;
}
var eventPaging:CustomEvent = new CustomEvent("EVENT_PAGING");
eventPaging.setParam("OFFSET", String(offset));
dispatchEvent(eventPaging);
}
}
}


・CustomEvent.as

package src.control
{
import flash.events.Event;

public class CustomEvent extends Event
{

private var parameter:Array = new Array();

public function CustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}

public function setParam(key:String , value:String):void{
parameter[key] = value;
}

public function getParam(key:String):String{
return parameter[key];
}

}
}


・main.css
/* CSS file */
.labelPageFirst
{
font-size:10pt;
font-weight:normal;
}
.labelPagePrev
{
font-size:10pt;
font-weight:normal;
}
.labelPageNext
{
font-size:10pt;
font-weight:normal;
}
.labelPageLast
{
font-size:10pt;
font-weight:normal;
}
.labelPageFirstMouseOut
{
font-size:10pt;
font-weight:normal;
}
.labelPagePrevMouseOut
{
font-size:10pt;
font-weight:normal;
}
.labelPageNextMouseOut
{
font-size:10pt;
font-weight:normal;
}
.labelPageLastMouseOut
{
font-size:10pt;
font-weight:normal;
}
.labelPageFirstMouseOver
{
font-size:10pt;
color: #ff7f50;
}
.labelPagePrevMouseOver
{
font-size:10pt;
color: #ff7f50;
}
.labelPageNextMouseOver
{
font-size:10pt;
color: #ff7f50;
}
.labelPageLastMouseOver
{
font-size:10pt;
color: #ff7f50;
}


・BlogService.PHP

<?php

require_once dirname(dirname(__FILE__)) . '/resource/example.inc.php';
require_once dirname(dirname(__FILE__)) . '/condition/BlogPagerCondition.php';
require_once dirname(dirname(__FILE__)) . '/helper/CustomPagerSupport.php';
require_once dirname(dirname(__FILE__)) . '/helper/S2Dao_PagerSupportCopy.class.php';
require_once 'Zend/Log.php';
require_once 'Zend/Log/Writer/Stream.php';
require_once 'Zend/Debug.php';

class BlogService {

function __construct(){
}

/**
* BLOGテーブルのリストとページング情報を返します。
*/
public function selectByCondition($title, $offset , $pageLimit){

//ログ出力
$writer = new Zend_Log_Writer_Stream('../../log/sercvice.log');
$o_log = new Zend_Log($writer);
$o_log->addFilter(new Zend_Log_Filter_Priority(Zend_Log::DEBUG));
//TODO パラメータチェック

$o_log->log('title:'.$title, Zend_Log::INFO);
$o_log->log('offset:'.$offset, Zend_Log::INFO);
$o_log->log('pageLimit:'.$pageLimit, Zend_Log::INFO);

//------ コンテナ初期化
$container = S2ContainerFactory::create(dirname(dirname(__FILE__)) ."/resource/example.dicon");
//ページャー取得
$pager = new S2Dao_PagerSupportCopy($pageLimit, 'BlogPagerCondition', 'blogPagerCondition');

//検索用のDtoを取得
$dto = $pager->getPagerCondition();
$o_log->log('dto class name :' . get_class($dto), Zend_Log::DEBUG);

//検索条件の設定
if (!is_null($title) && strlen($title) > 0) {
$dto->setTitle($title);
}
//オフセットの設定
if(!is_null($offset) && strlen($offset) > 0 ){
$dto->setOffset($offset);
}
else
{
$dto->setOffset(0);
}
//テーブルの検索
$blogDao = $container->getComponent("BlogDao");
$result = $blogDao->findByBlogPagerConditionArray($dto);

//検索結果をオブジェクトにセット
if($result == null){
return '0件です';
}
else
{
$helper = new S2Dao_PagerViewHelper($dto);
$resultObject = $this->getResultObject($result, $helper, $o_log);
return $resultObject;
}
}

/**
* 検索結果のリスト情報とオフセット情報をセットして返します。
* @param $record
* @param $helper
* @param $o_log
* @return object
*/
private function getResultObject($result, $helper) {

$resultArray =array();
foreach($result as $record ){
$resultArray[] = $record['BlogEntity'];
}
$offsetInfoArray = array(
'COUNT'=>$helper->getCount(), //全レコード数
'LIMIT'=>$helper->getLimit(), //1ページに表示するレコード数
'OFFSET'=>$helper->getOffset(), //レコードのオフセット
'IS_NEXT'=>$helper->isNext(), //次のボタンを表示できるか
'IS_PREV'=>$helper->isPrev(), //戻るのボタンを表示できるか
'IS_PREV'=>$helper->isPrev(), //戻るのボタンを表示できるか
'CURRENT_LAST_OFFSET'=>$helper->getCurrentLastOffset(), //現在表示中の一覧の最後のoffset
'NEXT_OFFSET'=>$helper->getNextOffset(), //次へリンクのoffset
'PREV_OFFSET'=>$helper->getPrevOffset(), //前へリンクのoffset
'PAGE_INDEX'=>$helper->getPageIndex(), //現在のページ番号
'PAGE_COUNT'=>$helper->getPageCount(), //現在ページのカウント(PAGE_INDEX+1)
'LAST_PAGE_INDEX'=>$helper->getLastPageIndex(), //最終ページのインデックス
'DISP_PAGE_INDEX_BEGIN'=>$helper->getDisplayPageIndexBegin(), //ページリンクの表示上限を元に、ページ番号リンクの表示開始位置
'DISP_PAGE_INDEX_END'=>$helper->getDisplayPageIndexEnd() //ページリンクの表示上限を元に、ページ番号リンクの表示終了位置
);
$resultMap = array('RESULT'=>$resultArray, 'OFFSET_INFO'=>$offsetInfoArray );
return $resultMap;
}

}
?>


・BlogPagerCondition.php
<?php
class BlogPagerCondition extends S2Dao_DefaultPagerCondition{

private $title;
public function getTitle(){
return $this->title;
}
public function setTitle($value){
$this->title = $value;
}

}
?>


・S2Dao_PagerSupportCopy.php
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
// +----------------------------------------------------------------------+
// | PHP version 5 |
// +----------------------------------------------------------------------+
// | Copyright 2005-2007 the Seasar Foundation and the Others. |
// +----------------------------------------------------------------------+
// | Licensed under the Apache License, Version 2.0 (the "License"); |
// | you may not use this file except in compliance with the License. |
// | You may obtain a copy of the License at |
// | |
// | http://www.apache.org/licenses/LICENSE-2.0 |
// | |
// | Unless required by applicable law or agreed to in writing, software |
// | distributed under the License is distributed on an "AS IS" BASIS, |
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, |
// | either express or implied. See the License for the specific language |
// | governing permissions and limitations under the License. |
// +----------------------------------------------------------------------+
// | Authors: yonekawa |
// +----------------------------------------------------------------------+
// $Id$
//
/**
* DTOのセッションへの格納をサポートします。
* @author yonekawa
*/
class S2Dao_PagerSupportCopy
{
/** 最大取得件数 */
private $limit;

/** 検索条件クラス名 */
private $pagerConditionClass;

/** 検索条件オブジェクトのセッション中の名前 */
private $pagerConditionName;

/**
* コンストラクタ
* セッションを開始して、指定された最大取得件数,検索条件クラス名,
* 検索条件オブジェクトのセッション中の名前で新しいPagerSupportインスタンスを生成します
* @param limit 最大取得件数
* @param pagerConditionClass 検索条件クラス名
* @param pagerConditionName 検索条件オブジェクトのセッション中の名前
*/
public function __construct($limit, $pagerConditionClass, $pagerConditionName)
{
if (!headers_sent() && session_id() == '') {
session_start();
}
$this->limit = $limit;
$this->pagerConditionClass = $pagerConditionClass;
$this->pagerConditionName = $pagerConditionName;
}

/**
* Session中の検索条件オブジェクトのoffsetを更新します
* @param offset 更新Offset
*/
public function updateOffset($offset)
{
$pagerCondition = $this->getPagerCondition();
if (empty($offset)) {
$offset = 0;
}
$pagerCondition->setOffset($offset);
}

/**
* セッション中の検索条件オブジェクトを取得します。
* 検索条件オブジェクトが存在しない場合、新規に検索条件オブジェクトを生成します。
* @return 検索条件オブジェクト
*/
public function getPagerCondition()
{
$dto = $_SESSION[$this->pagerConditionName];

if (empty($dto)) {
$refClass = new ReflectionClass($this->pagerConditionClass);
$dto = $refClass->newInstance();
$dto->setLimit($this->limit);

$_SESSION[$this->pagerConditionName] = serialize($dto);
return $dto;
}
else
{
return unserialize($dto);
}

//return $dto;
}

}
?>

コメント

このコメントは管理者の承認待ちです
このコメントは管理者の承認待ちです
このコメントは管理者の承認待ちです

コメントの投稿

  • URL
  • コメント
  • パスワード
  • 秘密
  • 管理者にだけ表示を許可する

トラックバック

トラックバックURL:http://kevinjohnson2.blog69.fc2.com/tb.php/231-8cb767fb
Flex+PHP(AMFPHP+S2Dao.PHP+S2Pager.PHP)でDataGridのページングを実装するの続きですが、S2Dao_PagerSupportでセッションから取得したDtoのクラスが__PHP_Incomple...
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。