SQLの窓

2013年08月31日


PHP に GET で SQL の select 構文(UTF8 で urlencode)を与えて JSON 文字列を返す簡単な処理

eval を使って、動的に配列を作成する文字列を作って(データを埋め込む)、さらに eval で実際の配列を作成してから json_encode します。

データベースは、Windows の MySQL で、PHP も Windows なので、PDO の ODBC を使って処理しています。ですから、MySQL は utf-8 ですが、クライアントアプリは SHIFT_JIS を前提に環境作成しているので、ちょっと変則な文字列変換が行われています。

ただ、そこは本筋とは関係無いので差し替えて考えて下さい。

json_encode が、utf-8 前提なので、『?q=select構文』の日本語部分は utf-8 で urlencode が前提です。

テーブルの列名が日本語の場合は、自動的に item_連番 という形式に列名を変更して JSON に渡します。もし、意図的にその文字列を定義したい場合は、select 構文の列名に英数字(_と-は可)別名を与えて下さい。
<?php
header( "Content-Type: application/json; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

mb_language( "ja" );
mb_internal_encoding("utf-8");

// *********************************************************
// extension=php_pdo_odbc.dll ( php.ini )
// *********************************************************
$conf = array(
	"dns_driver" => "{MySQL ODBC 5.2w Driver}",
	"dns_server" => "localhost",
	"dns_db" => "lightbox",
	"dns_user" => "root",
	"dns_pass" => "パスワード",
	"dns_charset" => "cp932"
);

// **********************************************************
// 接続
// **********************************************************
try {
	$cn1 = new PDO(
		"odbc:Driver={$conf['dns_driver']};Server={$conf['dns_server']};" .
		"Database={$conf['dns_db']};Uid={$conf['dns_user']};Pwd={$conf['dns_pass']};".
		"charset={$conf['dns_charset']}");
}
catch ( Exception $ex ) {
	print '{"error": "接続できませんでした: ' . $ex->getMessage() . '"}';
	exit();
}

//print "<pre>";
//print "接続されました\n";
//print_r($conf);

//print_r($cn1);

// **********************************************************
// SQL
// **********************************************************
$query = mb_convert_encoding( $_GET["q"], "sjis", "utf8" );

// **********************************************************
// 結果セット
// **********************************************************
$rs = $cn1->query( $query );
if ( $rs === false ) {
	$er = $cn1->errorInfo();
	print '{ "error" : "' . $er[2] . '" }';
	exit();
}

// フィールド数
$field_cnt = $rs->columnCount();
// 文字列バッファ
$buffer = "";
$update_cnt = 0;
while( $result = $rs->fetch(PDO::FETCH_ASSOC) ) {
	// ループの最初で列名一覧を作成する
	if ( $buffer == "" ) {
		// 配列に列名を取得
		$field_names = array_keys($result);
		$entry = build_field( $field_names );
		for( $i = 0; $i < $field_cnt; $i++ ) {
			if ( $buffer != "" ) {
				$buffer .= ",";
			}
			$buffer .= $field_names[$i];
		}
	}

	// ***************************************************************
	// 最終 eval に埋め込む為の変数セット( フィールド個々の内容 )
	// $myfield(n) という変数に実際の値をセットしておいて、
	// $myfield(n) という変数を最終文字列に埋め込んで eval する
	// ***************************************************************
	for( $i = 0; $i < $field_cnt; $i++ ) {
		$wk = "$" . "myfield" . ($i+1) . ' = "' . mb_convert_encoding( $result[$field_names[$i]], "utf8", "sjis" ) . '";';
		eval($wk);
	}

	// JSON 文字列に変換する為の配列を作成する文字列
	$x = '$my_json["items"][] = array( ' . $entry  .  ');';
	// eval で $my_json["items"] を作成
	eval($x);
}

// JSON 文字列化
$my_json_string =json_encode($my_json);
print $my_json_string;


// *********************************************************
// フィールド文字列( 連想配列部分作成 )
// 列名が日本語の場合は、item_(n) に変更する
// 指定したい場合は select の 列名記述を as で作成する
// *********************************************************
function build_field( $fields ) {

	$buffer = "";

	$field_cnt = count( $fields );

	for( $i = 0; $i < $field_cnt; $i++ ) {
		if (preg_match("/[^0-9a-zA-Z_\-]+/", $fields[$i])) {
			$field_name = "item_" . ($i+1);
		}
		else {
			$field_name = $fields[$i];
		}
		if ( $buffer != "" ) {
			$buffer .= ",";
		}
		$buffer .= '"' . $field_name . '" => "' . "$" . "myfield" . ($i+1) .  '"';
	}

	return $buffer;

}
?>

関連する記事

PHP : PDO : SQLExpress(SQLServer)の読み込みと同時更新
VB.net 2008 : System.Data.Odbc 接続テスト( MySQL 5.1 )


▼ 『?q=select+%2A+from+%E7%A4%BE%E5%93%A1%E3%83%9E%E3%82%B9%E3%82%BF
+limit+4』で出力したもの 
{"items":[{"item_1":"0001","item_2":"\u6d66\u5ca1 \u53cb\u4e5f","item_3":"\u30a6\u30e9\u30aa\u30ab \u30c8\u30e2\u30e4","item_4":"0003","item_5":"0","item_6":"2013-05-04 00:00:00","item_7":"2013-07-20 00:00:00","item_8":"270000","item_9":"9000","item_10":""},{"item_1":"0002","item_2":"\u5c71\u6751 \u6d0b\u4ee3","item_3":"\u30e4\u30de\u30e0\u30e9 \u30d2\u30ed\u30e8","item_4":"0003","item_5":"1","item_6":"2013-02-06 00:00:00","item_7":"2013-05-10 00:00:00","item_8":"300000","item_9":"","item_10":""},{"item_1":"0003","item_2":"\u591a\u5ca1 \u51ac\u884c","item_3":"\u30bf\u30aa\u30ab \u30d5\u30e6\u30e6\u30ad","item_4":"0002","item_5":"0","item_6":"2013-04-05 00:00:00","item_7":"2013-07-06 00:00:00","item_8":"250000","item_9":"","item_10":""},{"item_1":"0004","item_2":"\u9ad8\u7530 \u51ac\u7f8e","item_3":"\u30bf\u30ab\u30bf \u30d5\u30e6\u30df","item_4":"0003","item_5":"1","item_6":"2013-02-02 00:00:00","item_7":"2013-05-27 00:00:00","item_8":"250000","item_9":"","item_10":""}]}

▼ 以下は整形したもの
{
    "items": [
        {
            "item_1": "0001",
            "item_2": "浦岡 友也",
            "item_3": "ウラオカ トモヤ",
            "item_4": "0003",
            "item_5": "0",
            "item_6": "2013-05-04 00:00:00",
            "item_7": "2013-07-20 00:00:00",
            "item_8": "270000",
            "item_9": "9000",
            "item_10": ""
        },
        {
            "item_1": "0002",
            "item_2": "山村 洋代",
            "item_3": "ヤマムラ ヒロヨ",
            "item_4": "0003",
            "item_5": "1",
            "item_6": "2013-02-06 00:00:00",
            "item_7": "2013-05-10 00:00:00",
            "item_8": "300000",
            "item_9": "",
            "item_10": ""
        },
        {
            "item_1": "0003",
            "item_2": "多岡 冬行",
            "item_3": "タオカ フユユキ",
            "item_4": "0002",
            "item_5": "0",
            "item_6": "2013-04-05 00:00:00",
            "item_7": "2013-07-06 00:00:00",
            "item_8": "250000",
            "item_9": "",
            "item_10": ""
        },
        {
            "item_1": "0004",
            "item_2": "高田 冬美",
            "item_3": "タカタ フユミ",
            "item_4": "0003",
            "item_5": "1",
            "item_6": "2013-02-02 00:00:00",
            "item_7": "2013-05-27 00:00:00",
            "item_8": "250000",
            "item_9": "",
            "item_10": ""
        }
    ]
}



posted by lightbox at 2013-08-31 22:55 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

Android : Google Spreadsheets API version 3.0でGoogleスプレッドシートを参照

SkyDrive へ移動



Google のライブラリのダウンロード

gdata-java-client

かなり多くのライブラリが中に入っていますが、実際使うのは 5つです。

1) gdata-client-1.0.jar
2) gdata-core-1.0.jar
3) gdata-spreadsheet-3.0.jar
4) guava-11.0.2.jar
5) jsr305.jar

1〜3 は、lib ディレクトリ内で、4〜5 は、deps 内です。Google 的には、gdata を使うのは spreadsheet のみのようで、他の機能( 例えばカレンダー ) は他の API を使うようになっています。また、認証も本来は OAuth を推奨しているようですが、機能の説明に重点を置くために、簡単にユーザーとパスワードのログインで処理しています。


package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;

import android.os.AsyncTask;
import android.widget.EditText;

public class Android_Spreadsheet {

	// AsyncTask のインラインで参照する為の final
	private final String _userid;
	private final String _password;

	// **************************************************************
	// コンストラクタ
	// **************************************************************
	public Android_Spreadsheet(String _userid, String _password) {
		this._userid = _userid;
		this._password = _password;
	}

	// **************************************************************
	// AsyncTask の onPostExecute から外部イベントとして呼び出す為のインターフェイス
	// **************************************************************
	public interface SendMailed {
		public void onTweetResult(List<CellEntry> result);
   	}
	
	// **************************************************************
	// 通信処理
	// **************************************************************
	public void GetCells(String FileName, String SheetName,final SendMailed OnTweetResult) {
		
		new AsyncTask<String, Void, List<CellEntry>>() {

			// **************************************************************
			// 非同期処理
			// **************************************************************
			@Override
			protected List<CellEntry> doInBackground(String... params) {
				
				List<CellEntry> cells = null;

				String result_string = "";
				try {
					
					// *************************************************
					// ClientLogin
					// *************************************************
				    String USERNAME = _userid;
				    String PASSWORD = _password;

				    SpreadsheetService service = new SpreadsheetService("MySpreadsheetIntegration-v1");
				    service.setUserCredentials(USERNAME, PASSWORD);
				    
					// *************************************************
				    // バージョン3
					// *************************************************
				    SpreadsheetService sss = new SpreadsheetService("MySpreadsheetIntegration");
				    sss.setProtocolVersion(SpreadsheetService.Versions.V3);
				    
					// *************************************************
				    // 一覧を取得
					// *************************************************
				    URL SPREADSHEET_FEED_URL = new URL(
				        "https://spreadsheets.google.com/feeds/spreadsheets/private/full");
				    SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
				    // 一覧用のリスト
				    List<SpreadsheetEntry> spreadsheets = feed.getEntries();
				    
				    // スプレッドシート一覧より目的のインデックスを取得
				    int target = 0;
				    for (SpreadsheetEntry Spreadsheet : spreadsheets) {

				    	System.out.println( Spreadsheet.getTitle().getPlainText() );
				    	
				    	if ( Spreadsheet.getTitle().getPlainText().equals(params[0]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    

				    SpreadsheetEntry spreadsheet = spreadsheets.get(target);
				    WorksheetFeed worksheetFeed = service.getFeed(
					        spreadsheet.getWorksheetFeedUrl(), WorksheetFeed.class);
				    List<WorksheetEntry> worksheets = worksheetFeed.getEntries();

				    target = 0;
				    for (WorksheetEntry worksheet : worksheets) {

				    	System.out.println( worksheet.getTitle().getPlainText() );
				    	
				    	if ( worksheet.getTitle().getPlainText().equals(params[1]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    
				    WorksheetEntry worksheet = worksheets.get(target);

				    URL cellFeedUrl = worksheet.getCellFeedUrl();
				    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
				    cells = cellFeed.getEntries();
					
				}
				catch( Exception e ) {
					System.out.println(e.getMessage());
				}
				
				return cells;
			}

			// **************************************************************
			// 非同期処理終了後の処理( 画面へのアクセスが可能 )
			// **************************************************************
			@Override
			protected void onPostExecute(List<CellEntry> result) {
				// 引数のインターフェイス内のメソッドを呼び出す
				OnTweetResult.onTweetResult(result);
			}
			
		}.execute(FileName,SheetName);
	
	}

}

スプレッドシートの一覧や、シートの一覧等、それぞれで通信処理をするので、Android では AsyncTask を用いて全て一括で処理を行って、最後の一覧のみを終了イベントとして処理するようにしています。

package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.example.posttest.Android_Spreadsheet;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetFeed;

import android.os.Bundle;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends Activity {

	private Android_Spreadsheet as = null;
	private ArrayAdapter<String> adapter = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		as = new Android_Spreadsheet(
			"", // アカウント
			"" // パスワード
			);
		
		adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1);
		
	}

	public void postAction(View view) {
		
	    
	    new MessageBox(this,"確認","送信しますか?") {

			@Override
			void onYesClick(DialogInterface dialog, int which) {

				System.out.println("onYesClick");
				
				as.GetCells( "In Googleドライブ", "シート2" ,new Android_Spreadsheet.SendMailed() {
					
						@Override
						public void onTweetResult(List<CellEntry> result) {
							
							ListView listView = (ListView)MainActivity.this.findViewById(R.id.listView1);
							listView.setAdapter(adapter);
							
						    for (CellEntry cell : result) {
						    	
						    	adapter.add( cell.getCell().getValue().toString() );
						    	// System.out.println( cell.getCell().getValue().toString() );
						    	
						    }				    
						    
						}
				});
				
			}
			
		}.show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		// 選択されたメニュー項目の ID
		int menuId = item.getItemId();
		
		switch(menuId){
		case R.id.action_settings:
			System.out.println("action_settings が選択されました");
			break;
		case R.id.menu_action1:
			System.out.println("menu_action1  が選択されました");
			
			Intent intent = new Intent();
			intent.putExtra("data", "メッセージを別のページに引き渡します");
			intent.setClassName("com.example.posttest", "com.example.posttest.NextPage");
			startActivity(intent);		
			
			break;
		}

		return true;
	}		
	
}




posted by lightbox at 2013-08-31 01:10 | Android | このブログの読者になる | 更新情報をチェックする

2013年08月29日


C#(.NET) : Google Spreadsheets API version 3.0でGoogleスプレッドシートを参照

Google Spreadsheet は、Google ドライブに作成する事ができる Excel のような仕様の WEB アプリケーションです。WEB 上でデータを保存しておいて、インターネット経由で参照や更新が可能なのが Google Spreadsheets API です。( 他の API では、JavaScript より参照できるものもあます )

Google Spreadsheets API version 3.0

.NET ライブラリのダウンロード
( その他全てへのリンク )



ダウンロードしたライブラリは、直接参照する必要があります。本来 XML データでやりとりするものをライブラリ化しているので、あまり便利なものにはなっていませんが、『スプレッドシート』『ワークシート』を順々に一覧より取得するようにはなっていますので、後から解りやすい仕様ではあります。

ここでは、Dictionary を使って保存する手順を行っていますが、使いやすくするには最初に全てのワークシートまでを Dictionary に保存しておいて、名前で取得できるクラスを作成するといいと思います。

▼ で使った『In Googleドライブ』を公開したもの ( 取引先名は VLOOKUP を シート2から行っています )
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Google.GData.Client;
using Google.GData.Spreadsheets;
using System.Diagnostics;

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {

        public SpreadsheetsService service = null;
        public Dictionary<string, SpreadsheetEntry> db_Spread = new Dictionary<string, SpreadsheetEntry>();
        public Dictionary<string, WorksheetEntry> table_Spread = null;

        public Form1()
        {
            InitializeComponent();
        }

        // ********************************************************
        // ログインデータを設定
        // ********************************************************
        private void button1_Click(object sender, EventArgs e)
        {
            service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
            service.setUserCredentials(this.textBox1.Text, this.textBox2.Text);

            Debug.WriteLine(service.ServiceIdentifier.ToString());
            
        }

        // ********************************************************
        // Google Spreadsheet 一覧をディクショナリに保存
        // ********************************************************
        private void button2_Click(object sender, EventArgs e)
        {
            this.listBox1.Items.Clear();

            SpreadsheetQuery query = new SpreadsheetQuery();

            try
            {
                SpreadsheetFeed feed = service.Query(query);
                foreach (SpreadsheetEntry entry in feed.Entries)
                {
                    Debug.WriteLine(entry.Title.Text);
                    this.listBox1.Items.Add(entry.Title.Text);
                    db_Spread.Add(entry.Title.Text, entry);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        // ********************************************************
        // ListBox の対象行をダブルクリックしてディクショナリにシートを保存
        // ********************************************************
        private void listBox1_DoubleClick(object sender, EventArgs e)
        {
            string target = this.listBox1.SelectedItem.ToString();
            Debug.WriteLine(target);

            try
            {
                SpreadsheetEntry target_Spread = db_Spread[target];

                table_Spread = new Dictionary<string, WorksheetEntry>();
                WorksheetFeed wsFeed = target_Spread.Worksheets;
                foreach (WorksheetEntry entry in wsFeed.Entries)
                {
                    table_Spread.Add(entry.Title.Text, entry);
                    Debug.WriteLine(entry.Title.Text);
                }

                // 先頭シートからデータを取得
                CellQuery cellQuery = new CellQuery(((WorksheetEntry)wsFeed.Entries[0]).CellFeedLink);
                CellFeed cellFeed = service.Query(cellQuery);

                uint nRow = 1;
                string strResult = "";
                foreach (CellEntry cell in cellFeed.Entries)
                {
                    if (nRow == cell.Row)
                    {
                        if (strResult == "")
                        {
                            strResult += cell.Value;
                        }
                        else
                        {
                            strResult += "," + cell.Value;
                        }
                    }
                    else
                    {
                        strResult += "\r\n" + cell.Value;
                    }
                    Debug.WriteLine(cell.Value);

                    nRow = cell.Row;
                }

                this.textBox3.Text = strResult;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }
    }
}




posted by lightbox at 2013-08-29 01:18 | API | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり

フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり