事務員、プログラマーになる

事務員がプログラマーになりました。

抽象クラスで遊んでみる②

abstractなクラスを5つ作ってみました。

ところでCI_Controllerを継承したMY_○○Controller.phpを5ファイル作ったら認識してくれませんでした。
私のやり方が悪いのか何なのかわかりませんが、結局MY_Controller.phpの中に5クラスかいたら全部認識してくれました。
これでいいのか…?

作ったのは、祖先クラス、検索画面用、新規登録画面用、編集画面用、選択画面用の4クラスです。
既にあった画面設計からこの4つに分類。各画面には複数のsubmitボタンがあるので、その分岐処理も親クラスに実装しました。

例えば新規登録画面用のコントローラーを継承すると、初期表示・確認画面表示・入力チェック・データの登録処理の4つのファンクション作成を強制されます。
確認画面表示は何かやりたいことがあった時用なので、ほとんどの画面は空っぽになっています。

親クラスでは子クラスの関数を呼び出す部分や、共通処理(重複起動チェック、権限チェック等)も記載しました。
あとは画面のヘッダーやフッターの読み込みも全部親クラスに任せてしまったので、画面を作ってる最中はその画面の処理だけに集中できるようになりました。

なかなか全体をルール付けられず苦労した!
でもどんどん開発スピードが上がって楽しい!
1ヵ月前に生まれて初めてプログラムをした時は1個画面作るのに1週間以上かかってたけど、検索画面なら1日で2~3画面作れるようになりました。

と、いう話を開発経験者の方にしてみたら
「そういうルールが少なくて自由度が高いのがCodeIgniterなんだけど・・・」と。
そ、そうなのか! フレームワークってそういう意味だったんだ!!
つまり開発するときは設計にあったフレームワークを探すか、フレームワークに合わせて設計すれば、この苦労はいらなかったってこと・・・なのかっ!

ま、勉強できたと思えばいいやー(*´з`)

IDEのコンソールにログを出したい

NetBeansを使って開発をしています。
先日、Javaという言語で開発している友人の環境を見せてもらうと、eclipseというIDEを使っていました。
eclipseIDEの下側に(コンソールというらしい)にログが出るんですよ。

頭の足りない私は、自分が書いたSQL等を幾度となく見失っておりまして。
先月MY_DB_query_builderを作成したときに身に着けた知識を生かして、SQLの実行時に変数とかが置き換わった後の実実行SQLデバッグログに出すようにしています。

ログファイルは日付で変わるので、探して開くの地味に面倒。
だからeclipseのコンソールにときめいたわけです。

NetBeansでデバッガ・コンソールを表示とやると、コンソールにはHTMLが出力されてしまい、CodeIgniterのログは出てくれません。
しばらくウンウン悩んだんですが、 ログファイルはNetBeansで開いたままでもどんどん追記される ことから、ログファイルをまるでコンソールのような顔で開けばいいじゃない!という結論に至りました。
↓こんな感じ。

f:id:system_ajisai:20170323171400p:plain

これを実現するためには、ファイルの日付が変わってもらってはこまります。

Logを出しているのはLog.phpというクラスのようだったので、これをMY_Log.phpとして拡張しました。 拡張ファイルは今まで通り、application\coreフォルダの下に配置。

write_logファンクションから、まずはファイル名を確定しているところを書き換えます。
忘れたまま本番環境に入れてしまうと怖いので、開発環境(私しか開発してない)だけ変わるようにしてみました。

  $filepath = $this->_log_path.'log-'.date('Y-m-d').'.'.$this->_file_ext;
  // 処理追加
  if(ENVIRONMENT == "development"){
     $filepath = $this->_log_path.'log_debug.'.$this->_file_ext;
  }

このままではlog_debugファイルがエラいサイズになってしまうので、2Mぐらいでバックアップしてくれるように書き加えます。

if ( ! file_exists($filepath))
{
    $newfile = TRUE;
    // Only add protection to php files
    if ($this->_file_ext === 'php')
    {
        $message .= "<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>\n\n";
    }
    
// 2017-03-23 add
} else {
    if( filesize($filepath) > 2097152 ){
        $backup_file = $this->_log_path.date('Y-m-d_His').'.php';
        copy($filepath, $backup_file);
        $newfile = TRUE;
    }
}

後ろの処理が実はよくわからなかったのですが、$newfile=TRUEにしておけば良きに計らってくれるようです。

これでログファイルをまるでコンソールのような顔で開いておけばあとはいい感じに計らってくれるようになりました。
悩みはログが更新されても一番下までスクロールしてくれないことですが・・・世の中妥協も必要ですね。

抽象クラスで遊んでみる①

前回勉強したabstractは、クラスだけではなくてファンクションにも使えるようです。

PHP: クラスの抽象化 - Manual

abstractクラスのabstractなファンクションは、子クラスで定義を強制する。

何のために!?

もう、気になって気になって。
それで、無い知恵絞って「abstractな親クラスが子クラスにファンクション定義を強制して嬉しい事ってなんだろう」と考えてみた結果、もしかしたら、親クラスから子クラスのファンクションが呼べるんだろうか…という気になってきました。
私の脳みそでは「親クラスが子クラスに確実に存在するファンクションがわかる」ぐらいの嬉しい事しか思いつかなかったんだもの!

親クラス

abstract class TestOyaClass extends CI_Controller {

    abstract public function show();
    
    public function index(){
        $this->show();
        echo '呼べたみたい';
    }
}

お子様クラス

class OkosamaClass extends TestOyaClass {    
    public function show(){
        echo '呼べてますか?';
    }
}

呼べました。
お子様クラスのコントローラー名/indexで普通に呼べてしまいました。
そうなんだー!!!

これって便利なんじゃないのかな。

今のシステムは更新ボタンを押すと毎回確認画面が表示されるんですが、開発済みソースを見ると入力チェック以外はおんなじことをやってるんですよね。 ということは、確認画面を出す処理を親クラスに書いて、入力チェックするabstractなファンクションを作れば、毎回作るのは入力チェックだけでいいんじゃないんだろうか。

ということで、ちょっとabstractなクラスを作っていきたいと思います。

SQLの「?」と戦う② 直す編

書いている構文から、「?」に検索条件を置き換えているのは

$this->db->query($sql, $binds)

のqueryで間違いないと思います。

systemフォルダの中をあさってみると
CI_DB_driverというクラスにqueryファンクションが存在しました。
さらに実際の置き換え処理をしているのがcompile_bindsファンクションのようです。

teratailで教えて頂いたサイト様を参考にCI_DB_driverを拡張しようとしたのですが、何度やってもうまくいきません。
わざと?を全部「あ」に置き換える処理にしてみても呼ばれてる気配がない。
これはおかしい・・・。

ということでソースをうんうん眺めていたのですが、気になったのが

abstract class CI_DB_driver {

abstractって…私が作ったクラスにはついてないけどなんだコレ。

抽象クラスというらしいですが、説明を何回読んでもサッパリ意味がわかりません。
わからないけど、要するにこれだけじゃ何もできなくて、継承しないと使えないもの?

その観点から「CI_DB_driver」でファイルを全部検索すると
↓こんなとこが見つかりました。

 if ( ! isset($query_builder) OR $query_builder === TRUE)
    {
        require_once(BASEPATH.'database/DB_query_builder.php');
        if ( ! class_exists('CI_DB', FALSE))
        {
            /**
             * CI_DB
             * Acts as an alias for both CI_DB_driver and CI_DB_query_builder.
             * @see    CI_DB_query_builder
             * @see    CI_DB_driver
             */
            class CI_DB extends CI_DB_query_builder { } // ここと
        }
    }
    elseif ( ! class_exists('CI_DB', FALSE))
    {
        /**
         * @ignore
         */
        class CI_DB extends CI_DB_driver { } // ここ
    }

おぉ…名前からして$this-dbのdbの本体がこのCI_DBのようだ・・・・。 いくらCI_DB_driverを拡張しても何の意味もないのは実態はCI_DBで、継承してるのは拡張してない本体だからという呼ばれないのかも。

そこでteratailでおしえて頂いたサイト様の以下を参考に a-zumi.net CI_DB_query_builderを継承したMY_DB_query_builder作成。

これで、本題だったcompile_bindsが上書きできるようになりました!!

↓こんな感じ

public function compile_binds($sql, $binds){
    $sql = parent::compile_binds($sql, $binds);
    
    if (array_values($binds) !== $binds) {
        foreach($binds as $key => $value){
            $escaped_value = $this->escape($value);
            if (is_array($escaped_value)){
                $escaped_value = '('.implode(',', $escaped_value).')';
            }
            $sql = str_replace(':'.$key, $escaped_value, $sql);
        }
    }
    return $sql;
}

最初にparentのファンクションを呼び出したので、?も置き換えてもらえました。 置き換え処理は親クラスを真似したのでたぶん大丈夫。 $escaped_valueをが配列かどうか判定している部分が何になるのか、さっぱりわかりません。 親クラスがやってたので真似しました。

これで目標は達成できましたが、abstractクラスというのが一体何のために使われるのかわかりません。 興味がわいて調べてみたら、ちょっと面白そうだったので、次はそれに挑戦してみます。

SQLの「?」と戦う① 問題点編

作っているのは社内用のシステムで90画面ぐらいある中の8画面がすでに作成されていました。
環境はPHPでCodeIgniter3というフレームワークが使用されています。

この辺の資料は、去年の11月ぐらいに中途で入ってわずか2週間で辞めた女性が残してくれていました。
感謝感謝(辞めたくなる気持ち、わかるよ)

最初に作るのは検索機能です。
「オマエがうるさいから検索条件いっぱい用意してやったから」
と言われるだけあって、設計書を見ると検索結果が画面外に出るようなことになってましたが
「いらないし邪魔なんで^^」と削除。
笑顔溢れるストレスフルな職場です☆

検索するためにはSQLというのを書かないといけないのですが、私が書こうとしたのがこんな感じ。

$_sql = "select * from table1 where id = ? AND name = ? "; 
$_binds = array('user123', 'username');
$this->db->query($_sql, $_binds)

$bindsに入っている値が前から順番に$sqlの?部分に置き換えられて実行されるらしいです。
ここで問題が発生。
私が欲しい検索条件は15個ぐらいあるんです(オッサンに邪魔とか言ったのに結局欲張った)
でも検索条件って毎回全部入力するわけじゃないじゃないですか。
そんな時にはWHEREに入れてほしくないんですが、ここをどういう風に書けばそうなるのかがわからなくて…。

無い知恵絞って考えついたSQL

$_sql = "select * from table1 where ( ? = '' OR id = ? ) AND ( ? = '' OR name = ? )";
$_binds = array('user123', 'user123', 'username', 'username');
$this->db->query($_sql, $_binds)

これによって同じ検索条件を2回ずつ渡す必要が発生。
結果、$_bindsの中身がズレたり足りなかったり、直したくてもどれがなんだかわからなくなったり。
困った挙句にteratailというところで質問させてもらいました。
teratail.com

世の中は初心者に優しい。
回答で情報を頂いたおかげで、無事に:id方式(シンボルというらしい?)に改造することができました。
備忘録を兼ねて、まずはそのことを書いておこうと思います。

※ちなみに既に開発済みだった8画面は、どうもCodeIgniter3の仲間に入っていないようです。
 ControllerもModelもなく1つのPHPファイルにバーーー!!と全部の処理が書いてあるようです。
 が、この時点の私は全部のソースがCodeIgniterの仲間だと思っています。

事務員、プログラマーになる。

我が社の「魔境」と言われるシステム部門に異動になってしまいました。 部署と言っても、50代前半の男性社員が1人しかいません。 同族経営の気質の強い小規模な会社で、そのオッサンは「かつて大手システム会社に勤めてた」副社長の親族らしいです。

このオッサンが曲者で、日本語が通じないことで有名です。 かつて何人も中途で人を雇ってもみんなすぐに辞めてしまう癖の強さに加え、全く使えないシステムを作るんです。 私は部署のパソコン入力事務をしていたので、かなり社内システムを使いますが、 「使いにくい」「遅い」「必要な機能がない(足りない)」の3重苦で、 結局Excelで別にデータを作って2重管理をせざるを得ない状態になっていました。

去年、ちょっと大きめのトラブルが発生した時の反省会議で、 「なぜデータを毎日入力しているのに予兆が見えなかったのか」ということがついに議題に上りました。 確かにデータは毎日入力しているんですが、データを検索する機能がないという 何のために毎日データ入力が必要なのかさっぱりわからないこの謎管理()システムのことが、 ついに公式の場で文句言う機会を得たのです!

そしたら、曲者オッサンが言ったセリフがこちら。 「トラブル発生でかかったコストと、この俺が必要かもわからない検索機能を付けるコストと、どっちがかかるとおもってるの?(笑)」

なんか無性にイラっ・・・・。

登録したデータの使い道がないのに何で毎日あのクッソ遅いシステムでデータ入れなきゃいけないのよ! コスト(笑)とかいうなら、あのゴミみたいなシステム作るのにかかったお金が一番の問題だろ。 ドブより最低なとこに捨てたとしか言いようがないわ!

というようなことを会議の場でポロっと言っちゃいまして(*´з`)

もろもろの後に、無事に検索ボタン押したら画面が8秒ぐらい動かなくなる挙句に 「検索上限を超えています」と言われて何も画面に出なてこない検索画面ができました。

で。今年に入って社長がやってきて。 「○○くん(曲者オッサン)が他の人の言うこと聞くのなんて初めてだよ~」 「しすあじちゃん、お願いだからシステム部門に行って面倒見てあげてくれない?」 と、日参状態でお願いされた挙句、事務職仲間からも 「アレと戦えるのしすあじちゃんだけだよ!」 「私なんか怖くて話もできないもん!」 「みんなを助けると思ってよ!!」

という経緯で、人身御供にささげられる運命になりました…。

私が意見を出して、オッサンが直すという構図が社長の思惑だったみたいですが 「いや、プログラムとか末端のヤツがやる仕事だから(笑) 俺はしない」 とオッサンが言い出し、やるやる詐欺で2年保留されているシステムの開発を 未経験(というか何の知識もない)私がやることになってしまいました!

でも、でも・・・・でもなんか楽しそうかも!

負けたくないので、勉強しながらいいもの作ってやるぜー!