Googleドライブの仕様が変わっていて「マイドライブ」で同期するローカルフォルダの変更に手間取ったお話
[2023/02/22]
こちらの記事の続きです。
フレームワークに頼らず素のPHPでオブジェクト指向を…(1)
収集したFeedデータはDBへの保存を想定しております。
が、SQLite かもしれないし、MySqlかもしれないし、MySqlでもWordPress用テーブルのどこかかもしれないし・・。
ということで、Interface と Abstract Class を作ってOOP的にシュッとしたヤツを・・と思ったものの、この辺りで、自分のデザインパターンに関する知識のなさが露呈してきます。
教科書に書かれてるデザインパターンとやらをきちんと使った記憶が無いんですよね。
SingletonとかFacade辺りはフムフムと学んでみるものの、フレームワークなしでスクラッチ設計する、なんてプロジェクトに関わったことないので、うっすらした知識だけで終わってます。
とは言え、手段に振り回されて疲れるのも嫌なので(むかーしEJBで疲弊してた同僚思い出した・・)、目的を達成しつつ、データの保存先が変わっても対応できる、ぐらいにしておけば良いかな・・ぐらいで進めます。
まずは ConcreteClass として
クラスを作って、DB接続のテストをしてみます。
(もしかして、データ取得部分はスタブにしておいて、Viewレイヤーからテスト+実装する、という手順の方が良いのかもしれないけれど。)
PHPUnit を使いましょ、ということで composer.json に以下を追加。
"require-dev": { "phpunit/phpunit": "^7" }
AbstractStorageクラスを継承した、
を作成し、テストロジック
を作成して、とりあえず connect できるところまでテスト。
それにしても PSR に従って AbstractStorage というクラス名にしたんだけれど、クラス名に Abstractって単語が明確に含まれている抽象クラスに対して AbstractStorage::getInstance() って呼ぶのは、なんだか違和感があるんだけれど考えすぎだろうか。
抽象クラスのクラス名を単に Storage にして、 Storage::getInstance() ってしたくなるのだけれど・・。
なんだか気持ち悪かったので、Storage クラスを別に作って、呼び出すときには Storage:getInstance() で呼ぶようにしてみました。
Storage::getInstance() 内では、Config で指定されたDBタイプを判断して、それ用のClass(StorageMySqlなど)を生成して返す、なんていう風にしてみました。
が、このためにわざわざ1クラス準備するのもずいぶん冗長な気がするなぁ・・。
どうするか悩んだのだけれど、結局元に戻すことに。
「しばらく時間が経って、すっかり記憶をなくしているアンポンタンな自分(もしくは経緯を知らない、自分ではない誰か)が見た時に「すんなり理解しやすいかどうか?」を考えると、AbstractStorage::getInstance() で呼び出すようにした方が、分かり易いだろうな、という判断です。
最終的にはWordPressのプラグインにしたいので、データはMySqlに・・と思っていたんですが、WordPressのDBを汚染してしまう、というのはちょっと気が引けます。(自分のPlugin専用のTableを追加するのは気持ち悪いし、かといってWP_OPTIONSにシリアライズして大量のデータ突っ込むのもイヤですし・・)
考えてみれば、RSSフィードから取得したデータをキャッシュしておきたいだけなので、SQLite使えばいいや、ということで、もう一つ SQLite 用の ConcreteClass を追加しました。
connect() のロジックだけ書き換えればOK、ということで TemplateMethod パターンがうまい具合に使えているような気がします。
この時点では、Storageクラスに load() や save() みたいな読み書きメソッドが必要だろうなー、ぐらいにしか考えていませんでした。
(このあたり、結局あとで大幅に書き換えることになります。)
以前、CodeRetriet というイベントに参加した際に、「細かくテストと実装を繰り返していく」という、TDDのテンポみたいなものを体験しました。
今回も、
を繰り返していきます。
例えば、 Channel クラスを作るときは、こんなコードを書いてすぐテスト、とね。
class ChannelTest extends TestCase { public function testCreateChannel() { $channel_1 = new Channel('https://example.com/rss/feed'); $this->assertEquals($channel_1->getSubscribeUrl(), 'https://example.com/rss/feed'); // title is null. $this->assertNull($channel_1->getTitle()); } }
さて、途中経過を少々端折りまして・・。
コーディングしている間に、なんだか実装に違和感が出てきました。
Channel に対して、「今君が持っている Channel 情報をDBに保存してくれたまえ。」と命令する場合は、
$channel->save();
で良いと思うんですが、全 Channel の一覧を取得する場合は、誰に頼みましょう・・?
Channel モデルは、自分自身の Channel に関する属性は知っていても、他の Channel に関する情報は知らなそうですし。
となると $aggregator->getChannels(); でしょうか。
あれ、でも Channel の情報は DB から取得するはずで、となると Aggregator にもDBアクセスの機能を持たせないといけない?
Aggregator はコントローラー。でもDBへのアクセスはモデルが担当するらしいんだけど・・。
・・という訳で、MVC を意識しすぎなのか、よく分からなくなってきました。
悩んで進まないのも嫌なので、モヤモヤしながらもコーディング。
なんてことを考えて作っていった結果、 AbstractStoragePDO 内に Channel や Episode に対する操作メソッドがどっちゃりと増えてしまいました。
うーん、なんだか気持ち悪いけれど、ともあれ動くところまでは漕ぎ着けました、っとね。
ちなみに、最初のバージョンを仕上げた後に知ったのがこちら。これを読んでいたら、また違ったつくりになっていたのかもしれません(^_^;)
(しんばらさんのポッドキャスト「PHPの現場」オススメです。)
さて、View レイヤーを作り込んでいくときに、テンプレートエンジンを使ってみようと思い立ちました。
Laravel は既に触っていたので、blade の存在は知っていたんですが、ちょっと大げさな気がします。
軽量のもので何かないかな・・と探して見つけたのが、この記事。
なんと、テンプレートエンジンを自分で作る、というお話のよう。
せっかくなので、写経しつつテンプレートエンジンの作り方を学んでしまえ、ということで、こちらの記事に書かれているソースを使わせて頂きました。
なるほど、ファイル内の特定文字列を置換して、そいつを include してやれば動くってことなんですね。
と言う訳で、なんとか見られるものが作れたかなー・・、ということで、こちらで公開してみるのでした。
ソースは Github にてご覧いただけます。
実際に動くものはこちら。
ログ出力とかエラーハンドリングとか、まだまだな感じがあるんですが、取り急ぎ公開してみた、という感じです。アドバイスやコメントなどありましたら、お手柔らかに Twitter 等でお声がけ頂けたら嬉しいですm(_ _)m。
前回の記事の冒頭に書いたように、お仕事環境を変えようと目論んでおります。
もし「一度話だけでも聞いてやるか・・」という懐の深い会社さんがありましたら、お仕事募集の案内をこちら↓のページに記載しましたので、ご覧頂ければ幸いです。m(_ _)m