PHPでTemplate Method パターン

DBへの接続オブジェクトを継承した各テーブル毎のクラスでinsert() / update() 実装を想定

  • ハリウッドの法則
    • 親クラスが子クラスのメソッドを必要なタイミングで呼ぶ
    • 今回でいくと、regist(親クラス)から子クラスで実装したinsert / update をcallする
  • 同じ処理を集約し、抽象クラス、抽象メソッドとしてインターフェースを用意する
<?php

// DBへの接続、validateを行うようなDB処理のスーパークラスを想定
abstract class DBObject {
    private $target_object;
    public function __construct($obj) {
        $this->target_object = $obj;
    }

    public abstract function insert();
    public abstract function update();
    public function regist() {
        if ($this->target_object->id) {
            $this->update();
        } else {
            $this->insert();
        }
    }
}

// 各テーブルへの実装を、具象クラスでテーブル毎に行う想定
class ObjectA extends DBObject {
    public function insert() {
        // insert 処理
        echo "--insert--" , PHP_EOL;
    }

    public function update() {
        // update 処理
        echo "--update--" , PHP_EOL;
    }
}

// 実際に利用する場合は、idの有無でinsert / update を判定する事を想定
$sample = new stdClass();
$sample->id = 111; // idがありのため、updateを行う
//$sample->id = null;

$obj = new ObjectA($sample);
$obj->regist(); // cliantからはオブジェクトを生成し、registメソッドをcallするのみ。

雑感

さっと思った事を実装してみたが、Template Methodパターンにマッチする仕様なのか不明な部分が残る
他パターンをサンプル実装していく中で、戻って修正する事もあると思う
何か気になる箇所があったら是非コメント下さい

PHPでStrategyパターン

使うメリット

  • メール、SMS、プッシュ通知、今後増える可能性がある通知手段を、他実装に影響せず追加が行える

2016/12/11 に追記し、本記事最下部に変更したソースあり

<?php

// 抽象クラス
abstract class MessageStrategy {
    public abstract function send();
}

// コンテキストクラス
class MessageContext {
    private $strategy;

    public function __construct(MessageStrategy $message) {
       $this->strategy = $message;
    }

    public function send() {
        $this->strategy->send();
    }
}

// 具象クラス
class ConcreteMailMessage extends MessageStrategy {
    public function __construct() {
        // 前処理
    }

    public function send() {
        echo 'mail'.PHP_EOL;
    }
}
class ConcreteSMSMessage extends MessageStrategy {
    public function __construct() {
        // 前処理
    }

    public function send() {
        echo 'sms'.PHP_EOL;;
    }
}
class ConcretePushNotificationMessage extends MessageStrategy {
    public function __construct() {
        // 前処理
    }

    public function send() {
        echo 'push'.PHP_EOL;;
    }
}

$strategy = new ConcreteMailMessage();
$contextMessage = new MessageContext($strategy);
$contextMessage->send();

これから調べる事

  • 送信時にユーザクラスからmail/tel/token などを取得したいが、どのクラスで行うか
  • Userクラスに依存するので、Strategyパターンを使うのではなく、違うパターンが正解かもしれない

追記 2016/12/11

  • Uesrクラスは具象クラスにコンストラクタインジェクションする事にした
    • コンストラクタで注入する事で、各通知処理の前処理が行えると考えた
    • 仮にアプリユーザというクラス(mail / name を持たずtokenだけ持つクラスを利用する事があっても、具象クラスのみ影響があるため、他クラスは影響を受けないと考えた
    • テストコードを追加したい
<?php

// 送信対象のUserクラス
class User {
    private $name;
    private $mail;
    private $tel;

    // 仮に利用するpropertyを定義
    public function __construct($name, $mail, $tel) {
        $this->name = $name;
        $this->mail = $mail;
        $this->tel  = $tel;
    }

    // 仮に名前だけ出力するためにgetter設置
    public function getName() {
        return $this->name;
    }

}

// 抽象クラス
abstract class MessageStrategy {
    protected $user;
    public abstract function send();
    public abstract function __construct(User $user);
}

// コンテキストクラス
class MessageContext {
    private $strategy;

    public function __construct(MessageStrategy $message) {
        $this->strategy = $message;

    }

    public function send() {
        $this->strategy->send();
    }
}

// 具象クラス(Mail)
class ConcreteMailMessage extends MessageStrategy {
    public function __construct(User $user) {
        $this->user = $user;
        // mailに関する前処理
        // ...
    }

    public function send() {
        echo 'mail->to('. $this->user->getName(). ')'. PHP_EOL;
    }
}

// 具象クラス(SMS)
class ConcreteSMSMessage extends MessageStrategy {
    public function __construct(User $user) {
        $this->user = $user;
        // smsに関する前処理
        // ...
    }

    public function send() {
        echo 'sms->to('. $this->user->getName(). ')'. PHP_EOL;
    }
}

// 具象クラス(PushNotification)
class ConcretePushNotificationMessage extends MessageStrategy {
    public function __construct(User $user) {
        $this->user = $user;
        // Push通知に関する前処理
        // ...
    }

    public function send() {
        echo 'push->to('.$this->user->getName(). ')'. PHP_EOL;
    }
}

// 送信対象のテストUserインスタンス
$user = new User('Pさん', 'test@example.com', '090xxxxyyyy');

// Mail送信
$strategy = new ConcreteMailMessage($user);

// $strategyにどのクラスを代入するかでMail / SMS / PushNotification とsend()の振る舞いを変える
// SMS送信
// $strategy = new ConcreteSMSMessage($user);

// Push通知
// $strategy = new ConcretePushNotificationMessage($user);

// Contextクラスにstrategyクラスを注入
$contextMessage = new MessageContext($strategy);

// 送信処理 注入されたオブジェクトのsend()メソッドをcall
$contextMessage->send();

// mail->to(Pさん) と出力される

TravisCIで実行したPHPUnitの結果がCoverallsに反映されない

TravisCI Build時のRowLogで、environment variablesをSetしろの文言あり。

Read environment variables
Requirements are not satisfied.
  - TRAVIS='true'
  - TRAVIS_JOB_ID='1713xxxxx'
  - CI_NAME='travis-ci'
  - COVERALLS_REPO_TOKEN='********(HIDDEN)'

Set environment variables properly like the following.
For Travis users:

  - TRAVIS
  - TRAVIS_JOB_ID

対応としてTravisCIのEnvironment VariablesにSet

TravisCI Repository > Settings > Environment Variables
参考:php-coveralls issue

export CI_BUILD_NUMBER="$TRAVIS_BUILD_NUMBER"
export CI_PULL_REQUEST="$TRAVIS_PULL_REQUEST"
export CI_BRANCH="$TRAVIS_BRANCH"

f:id:tbrhdys:20161029001340p:plain

こういう場合は、UIからではなく、.travis.yml の before_script; にexport書くのが良いのか。

gitでまとめてrevert

git push -f ではなくまとめてrevertする方法メモ(いつも忘れるし、push -f とか一人のときはやっちゃう)

流れ

  • revertをする(--no-edit つけてeditorを起動しない)
    • ここでresetのように過去のcommit指定する方法はないのか?
    • HEAD~5 みたいにかけるのか
  • rebase で squashする(=押しつぶす/統合する)
  • squashしたコメントをeditorで編集
  • push

参考にさせて頂きました

$ git log --oneline
2f05e27 aaa
d56fd0f bbb
8c1c711 ccc
f7d9739 ddd
00fdd3d eee

$ git revert --no-edit 2f05e27
$ git revert --no-edit d56fd0f
$ git revert --no-edit 8c1c711
$ git revert --no-edit f7d9739
$ git revert --no-edit 00fdd3d

$ git rebase -i HEAD~5

editor起動
pick => squash に書き換える

英語圏だと-(ハイフン)ではなくダッシュというようだ。--はダッシュダッシュとでも言うのか。(不明) squash a cockroach(ゴキブリを押し潰す)でsquashは完全に覚えた

PHPプロジェクト(Laravel)をHerokuへpush時にFailed to detect set buildpack

$ git push heroku master
Counting objects: 255, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (226/226), done.
Writing objects: 100% (255/255), 46.83 KiB | 0 bytes/s, done.
Total 255 (delta 110), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Failed to detect set buildpack https://github.com/heroku/heroku-buildpack-php
remote: More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure
remote:
remote:  !     Push failed
remote: Verifying deploy...
remote:
remote: !   Push rejected to xxxxxxxxxxxxxxxxx.
remote:
To https://git.heroku.com/xxxxxxxxxxxxxx.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/xxxxxxxxxxxxx.git'

確認する事

  • composer.lock がpush 対象になっているか
    • なぜかignore global でcomposer.lockを含めていた(過去追加したので経緯は覚えてない)
    • ignore 対象のファイルの場合は git add -f xxx
  • composer.json が存在するか
  • buildpack がheroku/php と指定されているか
    • Laravel のroot にはpackage.json が存在しNodeと勘違いが発生するらしい(明示的にPHPとする必要あり)
  • heroku laravel のドキュメントを読む

試した事

  • 上記を試したが改善せず
  • 結局は、git init -> heroku create -> git push heroku master を行い、git remote add でGithubを追加し、Githubリポジトリをmergeして回避
    • remote が複数ある事が原因ではない事は確認済だが、git init -> heroku create の順に実行する必要があるのか原因は不明

利用したコマンド

  • heroku config -- 設定状況を確認
  • heroku buildpacks -- buildpackが何か確認

Laravelからのメール送信時にgmailを利用する(local dev env)

Swift_TransportException in AbstractSmtpTransport.php line 383:
Expected response code 250 but got code "535", with message "535-5.7.8 Username and Password not accepted. Learn more at
535 5.7.8 https://support.google.com/mail/?p=BadCredentials v65sm18105484pfi.42 - gsmtp
"

メール送信時に上記エラーが発生した場合は、 Gmailの2段階認証:オン にして、アプリパスワード再発行

Gmailはログイン時のパスワードと外部でSMTPとして利用する際のパスワードが違う。 ログイン時のパスワードをLaravelのenvファイルに書いた場合に上記エラーが発生する。 (何年か前も書いた気がするが、忘れてたので再度書く)

参考にさせて頂きました

気がつくと、6月の投稿を最後に何も書いてなかった。 その間にRubyKaigi行ったり、AppleWatchS2使い始めたりとネタはあったはずだが。。。。。

最近は家でスタンディングデスク環境も作ったので、その辺も近々。 明後日から沖縄に行くので、帰ってからまとめる。

最近はTHE WALKING DEAD ばかり見てて、Season4に入ったところ。さすがに飽きて来てる。

Backlogから通知されたissueをmenubarに表示

BitBarを使ってBacklogの通知を表示again

  • 前回はshellでAPIを叩いたが、rubyで書いてみたかっただけの記事
  • and 通知から通知元ページに遷移したかった

f:id:tbrhdys:20160622200210p:plain

f:id:tbrhdys:20160622200213p:plain

必要なpackage

書いてみたソース

bitbar-backlog-notify

ちょい気になった所

  • submenuを選択した後に、選択した前のissueがmenubarに表示される謎の動き
  • 結果として、puts "---" のハイフン3つが必要 (documentに記載あり)