連想配列の同じkey名の値を加算する

前に覚えた func_get_args というメソッドを使える機会があったので、使った内容をメモ。

タイトルの通り、パラメタで渡された連想配列のうち、同じkey名の値を加算します。パラメタはいくつでも渡せます。 func_get_argsのいいところ。

    //-----------------------------------------------------------------
    // パラメタで渡された配列の、同じkey名の値を合算した配列を返却する
    //-----------------------------------------------------------------
    protected function _arrayAdd()
    {
        $ret = array();

        // パラメタが1つ以上ない場合は処理をしない
        $params = func_get_args();
        if (count($params) == 0 ) { return; }

        foreach ($params as $item) {
            if (!is_array($item)) { continue; }

            foreach ($item as $key => $value) { $ret[$key] += $value; }
        }

        return $ret;
    }

extractという関数を見つけた

wordpressのソースを見ていたら、extractという関数を見つけた。
詳細はこちら。http://php.net/manual/ja/function.extract.php

連想配列の、key名の変数に、vlaueの値をつっこんでくれる関数。
今まで、

list($key1, $key2, $key3) = explode($list);

のように書いてた部分が、

extract($list); 

というようにもかける。

listだと、変数の数を動的に増やしたいときに不便だと感じるときがあったので、そういうときの代替案として使えそう。

color me shopにページャーをつける

color me shopをいじる機会があったので、今回作ったページャーをご紹介。
テンプレートは、smartyなので馴染みがあり、やりやすかったです。

目指す表示はこんな感じ
<< 前のページへ 1 ・・ 4 5 6 ・・・ 10 次のページへ >>

   
<{* 現在表示中のページ数をcurrent_pageに設定 *}> <{ if $smarty.get.page == "" }><{ assign var="current_page" value="1" }><{else}><{ assign var="current_page" value=$smarty.get.page }><{/if}> <{* 前のページがある場合は、リンク化する *}> <{if $productlist_prev_page != ""}><< 前のページへ<{else}> << 前のページへ <{/if}> <{* ページ表示数で商品数を割り、ページ数を算出 *}> <{math equation="ceil($productlist_num/12)" assign="max_page"}> <{math equation="$current_page-1" assign="bef_page" }> <{math equation="$current_page+1" assign="aft_page" }> <{ assign var="page_flg" value="0" }> <{section name=pager loop=$max_page }> <{* 表示中のページに、専用のclassを付与する *}> <{ if $smarty.section.pager.iteration == $current_page }> <{$smarty.section.pager.iteration }> <{ else }> <{ if $productlist_sort_now == "p" }> <{ assign var="page_url" value=$productlist_sort_price|cat:"&page="|cat:$smarty.section.pager.iteration }> <{ elseif $productlist_sort_now == "n" }> <{ assign var="page_url" value=$productlist_sort_new|cat:"&page="|cat:$smarty.section.pager.iteration }> <{ else }> <{ assign var="page_url" value=$productlist_sort_def|cat:"&page="|cat:$smarty.section.pager.iteration }> <{ /if }> <{ if $max_page > 7 }> <{* 1ページ目と最終ページ、現在表示しているページの前後1ページをリンクとして表示 *}> <{ if in_array($smarty.section.pager.iteration, array(1,$bef_page, $aft_page, $max_page)) }> <{ assign var="page_flg" value="0" }> <{ $smarty.section.pager.iteration }> <{ elseif $page_flg == "0" }> <{ assign var="page_flg" value="1" }> ・・・ <{ /if }> <{ else }> <{ $smarty.section.pager.iteration }> <{ /if }> <{ /if }> <{/section}> <{* 次のページがある場合は、リンク化する *}> <{if $productlist_next_page != ""}>次のページへ >><{else}> 次のページへ >> <{/if}>

外付けHDDとMacの相性

結論から先にいいます。
lacieとかMac用ってうたわれているもの以外の外付けHDDを使うときは、Macをスリープしない設定にした方が無難と思われます。

家では、バッファローの外付けHDDを2つ使っています。USBで本体から電源を取るものが1つ(timemachine用)、電源別のものを2つ(Macの機能を使ってRAIDにしてます)。このうち、timemachine用のものと、RAIDにしているものの1つが認識しなくなったり、RAIDエラーが起きるようになりました。

調べてみたところ、lacieとかはmacがスリープすると連動してスリープする機能が付いているみたいなんですが、バッファローとかはそこまでサポートしてないので、timemachineを設定してたりして、バックアップ中にスリープしたりすると、バックアップに失敗することがあるということでした。

この現象は何回か発生していて、リトライを何回かすると成功してたので、気にしてませんでした。このとき調べておけばよかったですね。失敗を繰り返すうちに、外付けHDDがダメになってしまったようです。

あと、電源を外からとっている方が安全だと感じました。壊れ方を見た印象なので、裏付けはないのですが。USBで電源もまかなうものは認識すらされなくなってしまったので。

スリープはしない設定にする。電源はmacから取らない。
これを今後はやっていくつもりです。

macbook proの外部ディスプレイ

僕が使っているmacbook proは、MB133J/Aという型番のやつです。
外部ディスプレイをつなげて、デュアルモニタでやってるんですが、このモニタがもともとwindowsのデスクトップ用に買ったものなので、解像度が1280×1024なんですよね。なんか、macbook proの画面とバランスが悪い。

macbook proの画面いっぱいに広げた、ウィンドウがもっていけなかったりとかね。使い勝手もイマイチ。
なので、購入を考えてたんですが、フルHDの解像度に対応しているのかがわからなかった。
16:10のアスペクト比に対応してて、1920x1200とかそれ以上のもいけるので、問題なさそうだけど、フルHDは16:10ではないので、そこがひっかかったら。。。買った後にわかるとか嫌だ。

ということで、アップルストアに確認したところ、「もちろんいけますよ!」とのこと。
おー、液晶テレビが流行る前に購入したやつでも対応してくれててよかったー!

これを検討中。画面もツヤツヤしてないやつだから、作業用にはよさそう。
http://www.amazon.co.jp/gp/product/B002TKKMA2/ref=ord_cart_shr?ie=UTF8&m=AN1VRQENFRJN5

airtunesを使ってみた

自宅では、無線LANを利用するのに AirMac Expressを使ってます。
以前から、DJの先輩が、無線LANでiTunesの音楽をスピーカーに飛ばしてるってことを教えてもらっていたのですが、1Kの部屋ではなかなかメリットもなく、後手後手になってました。。で、今回やってみました!!

http://www.apple.com/jp/airmacexpress/features/airtunes.html
ここを見ながらやったんですが、AirMac Expressのステレオピンジャックを利用してアンプに接続し、iTunesの右下にあるメニューから、”AirMac Express”を選択すると、ステレオ経由で音が流れてきました。オーディオインタフェース経由でやってたんですが、これで有線接続しなくてもiTunesの音が楽しめます!

ここで、youtubeとか、radikoとかブラウザで音楽を楽しむことってありますよね。
iTunes以外でも、AirMac Express経由で音を出せないものなのか!?ということで調べたら、こんな記事が。
http://mylifelog.sunnyday.jp/wordpress/2010/03/21/647

この Airfoil っていうアプリを使うと、AirTunesをiTunes以外のアプリでも使えました。
とりあえず動作確認まで。有料アプリなので、もっとひろい部屋に引っ越したとき、購入しようと思います。
http://rogueamoeba.com/airfoil/mac/

他にもアプリを作成されてたので、これもメモ。
http://rogueamoeba.com/

Zend_Db_Selectのcolumnsメソッドではまった

Zend_Db_Selectのメソッドにcolumnsというものがあります。
selectする対象を後付で追加できるメソッドです。使い方はこんな感じ。

※ $adapterは、Zend_Db_Adapter_Pdo_Mysql。

$adapter->select()
        ->from(array("t1" => "hoge_table"), array("id", "account"))
        ->columns(array("password", "update_date"))
        ->where("account = ?", "testest");
        ->query()->fetchAll();

今回やっていたのは、2つのSQL文をUNIONで結合するという内容でした。
取得するカラムが、”id”, “account”, “state”, “update_date”として、UNIONで結合するのですが、結合する2つのSQL文の対象となるテーブルに、”state”っていうカラムがないために、後付で”0″をつけるっていうことをやっていました。(実際は複数のテーブルを結合していて、複雑なので、簡略化してます。)

$select1 = $adapter->select()
                   ->from(array("t1" => "hoge_table"))
                   ->from(array("t2" => "hogehogehoge_table"), array("name"))
                   ->where("t1.id = t2.account_id")

$select2 = $adapter->select()
                   ->from(array("t1" => "hogehoge_table"))
                   ->from(array("t2" => "hogehogehoge_table"), array("name"))
                   ->columns(array("state" => new Zend_Db_Expr(0)));

$adapter->select()->union(array($select1, $select2))->query()->fetchAll();

テーブルのイメージはこう。

create table hoge_table (
    id int , 
    account varchar(16), 
    state varchar(8), 
    update_date timestamp, 

    primary key `id`
);

create table hogehoge_table (
    id int, 
    account varchar(4), 
    update_date timestamp, 

    primary key `id`
);

create table hogehogehoge_table (
    id int auto_increment, 
    account_id int, 
    name varchar(128),  

    primary key `id`
);

こういう場合、columnsで追加したカラムは、取得結果でも、後ろに追加した形ででてきます。

--------------------------------------------
| id | account | update_date | name | state | 
--------------------------------------------
| 1  |  test   | 2010-11-12  | hoge  | valid  |
---------------------------------------------

しかし、もともとテーブルに含まれるものについては、Zend_Db_Selectを利用しているためか、SQL文が作成される際に、並び替えられていて、stateがどの位置にくるか分かりません。(ここが問題の原因となります。)
しかも、カラム名、カラム数が一致しているのでUNIONにもエラーがでません。

columnsを利用しなかったSQL文の取得結果が、

--------------------------------------------
| id | account | update_date | state | name | 
--------------------------------------------
| 3  |  test   | 2010-11-15  | valid  | aaaa  |
---------------------------------------------

という順番で取得された場合、UNIONの結果は、

--------------------------------------------
| id | account | update_date | state | name | 
--------------------------------------------
| 3  |  test   | 2010-11-12  | valid  | aaaa  |
---------------------------------------------
| 1  |  test   | 2010-11-15  | hoge  | valid  |
---------------------------------------------

となって取得されます。

項目名と違う値が入ってくるのです。今回は、互い違いになったのが数値と日付で、それをsumして合計値を取得してたために、全く予測できない値が取得されてしまいました。

解決策は、片方でcolumnsを使って、カラム名を追加した場合は、UNIONで結合するSQLは全て、同じカラム名は、columnsを使って追加する方法をとるということです。

今回の例だと。

$select1 = $adapter->select()
                   ->from(array("t1" => "hoge_table"), 
                          array("id", "account", "update_date"))
                   ->from(array("t2" => "hogehogehoge_table"), array("name"))
                   ->columns(array("state" => "t1.state"))
                   ->where("t1.id = t2.account_id")

$select2 = $adapter->select()
                   ->from(array("t1" => "hogehoge_table"))
                   ->from(array("t2" => "hogehogehoge_table"), array("name"))
                   ->columns(array("state" => new Zend_Db_Expr(0)));

$adapter->select()->union(array($select1, $select2))->query()->fetchAll();

と書くことです。または、columnsを使わない形で、このような書き方もできると思います。

$select1 = $adapter->select()
                   ->from(array("t1" => "hoge_table"))
                   ->from(array("t2" => "hogehogehoge_table"), array("name"))
                   ->where("t1.id = t2.account_id")

$select2 = $adapter->select()
                   ->from(array("t1" => "hogehoge_table"), 
                          array("id", "account", "state" => new Zend_Db_Expr(0), "update_date")))
                   ->from(array("t2" => "hogehogehoge_table"), array("name"))
                   ->columns(array("state" => new Zend_Db_Expr(0)));

$adapter->select()->union(array($select1, $select2))->query()->fetchAll();

テーブルのカラム数が多いとcolumnsで後付した方が、記述量が少なくなるので、どちらを使うかはその時のテーブルによるかなと思います。重要なのはcolumnsの利用の有無を揃えること。

この説明、うまくやるの難しいですね。。。

wordpressのwp_headとwp_footer

wp_head()とwp_footer()というメソッド。
wordpressのコア部分のメソッドなのですが、恥ずかしながら今まで知りませんでした。

javascriptを利用したプラグインを利用するときは、これらは必須です。
今回、テンプレートにlightboxを導入する際に動ず、調べていたら、ここに行き着きました。

wp_headは、通常はwordpressのバージョン情報等をmetaタグで表示するくらいですが、wp_headが呼ばれたタイミングで、事前に登録したメソッドを呼び出せる機能ももっているのです。この機能を利用して、pluginの動作に必要なcssとかjavascriptとかの記述を行っているpluginが多いのです。
wp_footerも同様です。こちらはjavascriptの実行文を書いてあることが多いです。

今回使ってたテンプレートには、このどちらもなかったので wp-lightpopが動作しなかったというのが、動作不良の原因でした。

pluginによっては、jqueryを読み込んだり、prototype.jsを読み込んだりするので、バッティングして動作しなくなることもありますし、自前でjqueryを読み込んでたりする上に、wp_headでさらに読み込まれるとおかしくなることもあります。この辺は、試しながら調整する必要がどうしても出てきそうです。

add_action(‘wp_head’, ‘自作メソッド名’) で、処理追加
remove_action(‘wp_head’, ‘自作メソッド名’)で、処理削除

自分でプラグインを作成するときは、この辺は覚えておくと便利ですね。

htaccessでredirectする際に、パラメタ(query string)を引き継ぐ

wordpressを使うとき等に、htaccessにはお世話になってますが、以前$_GETをrewrite_condでひっかけて、redirect先へ引き継ごうとして、断念していました。
どうしても、正規表現でquery string(?key=valueの部分)がひっかけれなくて。

その時は無理矢理、http://test.com/key/value/の形式にリダイレクトして、プログラム側で、REQUEST_URIから、パラメタ名と、値を取得したんですが、今回いいものを見つけました。

それが、QSAオプション。Query Strins Appendの略です。
これをつけておくと、リダイレクトをかけても、そのままquery stringも引き継いでくれます。しかも、redirect時に、自分でquery stringを付け足しても、うまいこと後付してくれる賢さです。

http://www.hoge256.net/2008/04/119.html

サンプルはこんな感じ。

RewriteEngine On

RewriteCond %{REQUEST_URI} ^/test/
RewriteCond %{REQUEST_URI} !\.(js|ico|gif|jpg|png|css|html)$
RewriteRule ^(.*)$ / [P,L,QSA]