2011年12月13日火曜日

FlashでPDF

■別に専門というわけではないけれど■

最近は変わりつつありますが、Webアプリにとって「カチッ」とした帳票というのはやや鬼門で、皆さんかなり苦労を強いられてきました。

私自身を振り返ってみてもPDF関連の実装が割と大きな比重を占めています。前職のWebObjectsでもiTextというJavaのライブラリを使っていましたし、現職Force.com屋でもほぼすべての案件にPDF生成が含まれている状況です。

これまでForce.com上で部門別部課別集計、罫線の各種アレンジ、複数ページ請求書などいろいろな帳票を作ってきましたが、日本ビジネス特有のきめ細かい帳票要求については、以下の2点:

  • 日本語フォントが1種類しか使えない
  • ひらがなカタカナの拗促音がbaselineに揃わない

を除けば、特に不自由なく生成できるので、ネタとしては面白くありません。


■FlashでPDF■

さて、Flash toolkit for Force.comつかっている人います? 日本のBBSを見てもほとんど話題が出ていないですし、海外の掲示板でもFlashBuilder for Force.comが中心になってしまったようで、大変寂しい思いをしています。toolkitの開発もすっかり止まってしまいましたし、このままfade outしちゃうんですかね…。

というわけで、この機会に少しでも開発者が増えることを願って、Flash+Force.comでのPDF生成について書きます。今回はFlash+Force.comについて、次回はFlash上でのPDF生成について書きます。


■Flashでの開発■

Force.com Toolkit for Adobe AIR and Flex

何よりFlashがないと話しになりません。とりあえず30日間無料でお試しができますので、Adobeから「FlashBuilder 4.6 Standard Edition」をダウンロードしてきてください。

余談ですが、4.5からはiPhone/Androidアプリも書くことができます。試してみたらホントに15分くらいでForce.com対応iPhoneアプリを書けて大笑いしたんですが、GUIがネイティブと異なりすぎることとメモリをバカ食いするので、今のところ本気では使っていません。

さて、お次は、FlashからForce.comをアクセスするためのライブラリが必要です。これは、このページの「download toolkit」から落としてきます。

では、FlashBuilderを起動してください。ユーザ登録の手続きなどは端折ります。起動遅いっす。私の嫌いなEclipse様がベースなので諦めてください。

ファイル>新規プロジェクト>Flexプロジェクト

適当な場所に適当な名前でプロジェクトを作ります。とりあえず「Advent」にしました。アプリケーションの種類をデスクトップにする以外はデフォルトのままでOKです。

できたプロジェクトにForce.comのライブラリを追加します。さっきダウンロードしてきたライブラリの中から「force-air.swc」をプロジェクトのlibsにドラッグ&ドロップしてください。

パッケージエクスプローラの中から

Adbent>src>(default package)>Advent.mxml

を開きます。ソースの先頭にあるs:WindowedApplicationとfx:Declarationsを以下のソースと置き換えてください。

<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
 xmlns:s="library://ns.adobe.com/flex/spark" 
 xmlns:mx="library://ns.adobe.com/flex/mx"
 xmlns:salesforce="http://www.salesforce.com/"
 applicationComplete="init()" height="560" width="800">
 
 <fx:Declarations>
  <salesforce:AIRConnection id="force"/>
 </fx:Declarations>


その後にはコード部分のタグが来て

 <fx:Script>
  <![CDATA[


   
  ]]>
 </fx:Script>

最後にデータを表示するためのグリッドが来て最初のタグを閉じます。

 <mx:DataGrid id="dataGrid" 
   top="10" left="23" right="23" bottom="10" 
   editable="false" enabled="false">
  <mx:columns>
      <mx:DataGridColumn headerText="id"  dataField="Id"/>
      <mx:DataGridColumn headerText="氏名" dataField="Name"/>
      <mx:DataGridColumn headerText="電子メール" dataField="Email"/>
  </mx:columns>
 </mx:DataGrid>

</s:WindowedApplication>

以上が基本的なソース構造です。で、「CDATA[」の次の行からコードを書いていきます。

■まずはインポートとグローバル変数■

 import com.salesforce.AsyncResponder;
 import com.salesforce.objects.LoginRequest;
 import com.salesforce.results.Fault;
 import com.salesforce.results.LoginResult;
 import com.salesforce.results.QueryResult;
   
 import mx.collections.ArrayCollection;

 [Bindable]
 private var arrayUser:ArrayCollection = null;


■初期化/ログイン■

冒頭部分にあるapplicationCompleteから呼び出される初期化ルーチン、ここでログイン処理をします。昔ながらの処理でOAuthなどはありませんorz。なお、これはデスクトップアプリなのでIDとPasswordでログイン処理を行いますが、FlashとしてVisualforceに埋め込む場合にはセッションidなどからそのまま認証を引き継ぐことができるようになっています。


 private function init():void 
 {
  var lr:LoginRequest = new LoginRequest();
  status = "login by id/password";
  
  lr.username = "your.account@your.domain";
  lr.password = "password" + "token";
  
  lr.callback = new AsyncResponder(loginHandler);
  force.login(lr);
 }  


■ログイン成功■

ログインが成功すると、この関数が呼び出されます。ここではすぐに馴染み深いSOQLを呼び出していますが、検索処理などを行う場合には、ここでGUIの「検索ボタン」をenableにする、などの処理を書きます。

 private function loginHandler(result:LoginResult):void 
 {
  if (result.userInfo != null) {
   var strQuery:String = "Select Id, Name, Email From User";
     
   force.query(strQuery, 
        new AsyncResponder(queryHandler, 
             faultHandler));
  }
 }


■Query成功■

Query成功時にこのコードが呼び出されます。ここは1回に最大2000レコードずつ渡されます。つまり逆にいえば、数万件のレコードがヒットするようなQueryを発行しても、ここでarrayUserにひとまとめにすることができる、ということです。まぁ遅いですけどね。それにFlashは基本的にシングルタスクなので何も配慮しないで数万件のPDF帳票を出力しようとするとUIが一切操作できなくなります。不憫な奴。

で、最後にdataGridに読み込んだデータを一括してセットすれば一丁上がり。

 private function queryHandler(result:QueryResult):void
 {
  if (result != null && result.records != null 
   && result.records.length > 0) 
  {
   if (arrayUser == null)
    arrayUser = new ArrayCollection();
   arrayUser.addAll(result.records);
  }
    
  if (!result.done)
  {
   trace("query again");
   force.queryMore(result.queryLocator, 
     new AsyncResponder(queryHandler));
  }
  else
  {
   status = "検索または実行開始をどうぞ";
   force.syncConnection
     
   dataGrid.dataProvider = arrayUser;
  }
 }


■エラー処理■

SOQLに誤りがあった場合などにこの関数が呼び出されます。

 private function faultHandler(result:Fault):void
 {
  trace(typeof(result));
  trace(result.faultcode);
  trace(result.faultstring);
 }

■基本編終了■

以上でForce.com + Flexの基本はおしまいです。次回はPDF生成編です。それまでよろしかったらSOQLを書き換えたりDataGridのdataFieldをいじったりして遊んでみてください。なお、"Select Id, Name, CurrentStatus From User"などと書いてもAPIが古いのでこのライブラリでは対応していません。あしからず。

--

この記事はForce.com Advent Calendarに参加しています。

Force.com Advent Calendar 2011 : ATND

PhoneGapかiOS SDKについて書くと予告していたのにFlashになったのは準備していたネタが土壇場になって不調だったからさ。以前書いた記事もあるし。

PhoneGap 1.2.0 + Salesforce SDK(hyblid)

以上、50歳と2日のくらはしがお送りしました。

#Bloggerさんがインデントをつぶしてしまったけど、まぁ読めるのでご勘弁

0 件のコメント:

コメントを投稿