孤独プログラマー譚

孤独死が近い。

Drupal8 FormクラスでGET通信を使う

カスタムモジュールで、Formクラスを使う時の話。

デフォルトはPOST通信になる。

「設定を変えればGET通信なんて余裕だぜ、はははー」
なんて思いつつ、以下に設定。

<?php
$form['#method'] = 'get';

うーん、だめ。動かない。なぜ?

悶々と調べた結果、Drupal8コアのバクとしか思えないんだが…。

以下、コアから引用。

<?php
// 'GET' が設定される?
$form_state->setRequestMethod($request->getMethod());

// 'GET' 時の分岐
<?php
$input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all();

実際、一つ目のメソッドで 'GET' は設定されない。

以下のように、setRequestMethod ではなく、setMethod を使う必要がある。

<?php
$form_state->setMethod($request->getMethod());

ということで、この辺りの処理を、コアから外に出して、手動で行う。

コントローラにて。

<?php
$formState = new FormState();
$formState->setMethod('get');
$form = \Drupal::formBuilder()->buildForm('\Drupal\sample_form\Form\ExampleForm', $formState);

上記のように書くと、何事も無かったかのように、FormクラスがGET通信で動く様になる。

JavaScriptのコンストラクタとプライベート変数

まず、コンストラクタを使うこと前提で。

コンストラクタ内でメソッドを定義する場合、プライベート変数を使うことができる。

よくあるカウント機能を持つサンプル。

function Nogi() {
    var count = 0;
    this.say = function () {
        count++;
        console.log(count);
    };
}

インスタンス毎に、プライベート変数 count が別々にカウントアップされていることが分かる。

var nogi = new Nogi();

nogi.say(); // 1
nogi.say(); // 2
nogi.say(); // 3

var nogi2 = new Nogi();

nogi2.say(); // 1
nogi2.say(); // 2
nogi2.say(); // 3

しかし、インスタンスを作るごとに、this.say()がコピーされてしまう。
普通、メソッドは prototype に定義する。

だが、prototypeでメソッドを定義する場合、プライベート変数を使うことはできない。

それっぽいことをやってみる。

function Nogi() {}

Nogi.prototype.say = (function () {
    var count = 0;
    return function () {
        count++;
        console.log(count);
    }
}());

インスタンスを複数作り、カウントアップ。

var nogi = new Nogi();

nogi.say(); // 1
nogi.say(); // 2
nogi.say(); // 3

var nogi2 = new Nogi();

nogi2.say(); // 4
nogi2.say(); // 5
nogi2.say(); // 6

変数 count が共用されている。そりゃ prototype 自体が共用されているので当たり前か。
count はクラス変数になっているとも言える。



ES6ならやりたいことが出来るかも。
でも今はES6のことは考えたくありません。さようなら。

Observerパターンについて考える

Observerパターン、名前は知ってるけど、いまいち使いどころがよく分かりませんでした。

「状態の変化を通知する仕組み」とのことですが、あまりそういう状況に出くわさないというか、頭にピンとこないというか…。

調べてみたんですが、おそらく自分は勘違いしてました。

これ、別に通知とか関係ないのでは。

結局のところ、

<?php
$objects = [
    new ObjectA(),
    new ObjectB(),
    new ObjectC()
];
foreach ($objects as $object) {
    $object->commonMethod();
}

上記のように、「同名のメソッドを持つなら、ループできるよ」というだけのように思います。

<?php
interface Object
{
    public function commonMethod();
}

インターフェースを作って、共通のメソッドを持たせます。


そしてもう一点、「状態変化をどのObserverに通知するか(共通メソッドをどのオブジェクトに実行させるか)」ですが、新人の頃の自分なら、わざわざフラグ用の変数を作っていたように思います。

<?php
$objectA->isObserving = true;
$objectB->isObserving = false;
$objectC->isObserving = true;

$objects = [$objectA, $objectB, $objectC];

foreach ($objects as $object) {
    if ($object->isObserving) { $object->commonMethod(); }
}

でもそうじゃなく、Subject(メインルーチン)にObserver(メソッドを実行させるオブジェクト)を配列で持たせてやれば、それでいいんですよね。

<?php
$subject->observerList = [
    $ObjectA, $ObjectC
];
foreach ($subject->observerList as $object) {
    $object->commonMethod();
}

フラグ用の変数を作るのは、今後控えようと思います。フラグを立てたいオブジェクトを配列に持たせてやれば、それでよい!

結局、JavaScriptはどう継承すればいいのか

結局、JavaScriptはどう継承すればいいのか。
調べてもサイトによって書いてる事が違うので、ひとまず、信用できそうな方法を以下にまとめます。
(非 ES6 です。)


乃木坂46秋元真夏を親クラス、相楽伊織を子クラスとします。
2期生の相楽は、1期生秋元の意思を継いでるわけですね。


prototype継承用の関数。
一時的なプロキシコンストラクタを使います。

// prototype継承用の関数
var inherit = (function() {
    var F = function() {}; // クロージャを利用
    return function(C, P) {
        F.prototype = P.prototype;
        C.prototype = new F();
        C.uber = P.prototype;
        C.prototype.constructor = C;
    }
}());


こちら、親クラス。

// 親クラス
var Parent = function() {
    this.group = '乃木坂46';
    this.name = '秋元真夏';
}
Parent.prototype.shoot = function() {
    console.log(this.group + 'の' + this.name + 'です。ズッキュン!');
}


子クラスです。
applyメソッドで、親コンストラクタのプロパティを継承。
inherit関数で、親のprototypeに設定されたメソッドを継承してます。

// 子クラス
var Child = function() {
    Parent.apply(this, arguments); // 親のプロパティを継承
    this.name = '相楽伊織';
}
inherit(Child, Parent); // 親のメソッドを継承


実行。相楽伊織がズッキュンを継承してくれました。

var child = new Child;
child.shoot(); // 乃木坂46の相楽伊織です。ズッキュン!


うーん、まだprototypeを使いこなしてる感じがしないなぁ。
もう少し粘着して覚えていきます。

デコレータパターンでテンプレートメソッドを使う(PHP)

うーん、今までデコレータパターンを勘違いしてた。
デコレータパターンは継承と書き換え可能でした。


デコレータは動的に継承っぽい関係を作れるので、その点メリットがあります。


ただ、どの程度書き換え可能なのか…?


自分はテンプレートメソッドをよく使うんですが、そういうこともデコレータは出来るのか…?


というわけで、デコレータパターンでテンプレートメソッドを再現してみました。


ひとまず、一旦、普通の継承を。

<?php
class Super {
    public function render() {
        echo '<p>2</p>';
        $this->tmplMethod();
        echo '<p>4</p>';
    }
}
class Child extends Super {
    public function render() {
        echo '<p>1</p>';
        parent::render();
        echo '<p>5</p>';
    }
    public function tmplMethod() {
        echo '<p>3</p>';
    }
}

$obj = new Child();
$obj->render();

こいつを、デコレータで書き換える。


親となるクラス。

<?php
class Super {
    public function render(callable $callback) {
        echo '<p>2</p>';
        $callback();
        echo '<p>4</p>';
    }
}


子供のクラス。

<?php
class Child {
    public function __construct(Super $super) {
        $this->super = $super;
    }
    public function render() {
        echo '<p>1</p>';
        $this->super->render([$this, 'tmplMethod']);
        echo '<p>5</p>';
    }
    public function tmplMethod() {
        echo '<p>3</p>';
    }
}


実行。

<?php
$obj = new Child(new Super);
$obj->render();

 
 
 
うーん、引数でコールバックを渡すくらいしか、思いつかないなぁ。
ひとまず、この方法で使っていきます。

JavaScriptを超シンプルなMVCで書く(2)

以前の記事「JavaScriptを超シンプルなMVCで書く」を見返すとイマイチなので書き直し。
lonely-programmer.hatenablog.jp



以下挙動のアプリケーションを作ります。

・ボタンをクリックする毎に、白石麻衣の所属するグループが、乃木坂46 / 欅坂46 / SKE46 の順番でループする。



まずモデル。

var model = (function() {

    /* privateメンバ */
    var count = 0;
    var groups = [];
    // 現在のグループを取得
    var getNowGroup = function() {
      return groups[getIndex()];
    };
    // 現在のインデックスを取得
    var getIndex =  function() {
      // 0 → 1 → 2 → 0 → 1 → 2... とインデックスをループ
      return count % groups.length
    };

    /* publicメンバ */
    // グループを追加
    var addGroup =  function(group) {
      groups.push(group);
    };
    // グループを変更
    var changeHerGroup = function() {
      count++;
      $(this).trigger('groupIsChanged');
    };
    // グループ名を表示
    var getGroupName = function () {
      return getNowGroup().getName();
    };

    // publicメンバを開示
    return {
      addGroup: addGroup,
      changeHerGroup: changeHerGroup,
      getGroupName: getGroupName
    };

})();

モジュールパターンでオブジェクトを返却してます。
このオブジェクトはクロージャです。
privateメンバには外部からアクセスできません。



注目すべきはここ。

var changeHerGroup = function() {
  count++;
  $(this).trigger('groupIsChanged');
};

モデル内の値に変化があった場合、'groupIsChanged'イベントが発火されます。



オブジェクトではなく、コンストラクタ関数で返却する場合は、メソッド後半を以下のように書き変えます。

// コンストラクタ関数にpublicメンバを登録
var Constr = function() {
  this.addGroup = addGroup;
  this.changeHerGroup = changeHerGroup;
};
// プロトタイプにpublicメンバを登録
Constr.prototype = {
  getGroupName: getGroupName
};
// コンストラクタ関数を返却
return Constr;

 



グループ用のオブジェクトをモデルのプロパティに追加。
変数nameはprivateです。

// グループ用のコンストラクタ関数
function Group(name) {
  var name = name; // 書かなくても良い
  this.getName = function() {
    return name;
  };
}
// グループ追加
model.addGroup(new Group('乃木坂46'));
model.addGroup(new Group('欅坂46'));
model.addGroup(new Group('SKE48'));

 


ボタンがクリックされると、モデル内の値を更新。

// ボタンクリック時のイベントハンドラ
$('button').on('click', function() {
  model.changeHerGroup();
});

 


カスタムイベントを作成。

// レンダリング用カスタムイベント
$('span').on('render', function() {
  $(this).text(model.getGroupName());
});

 


モデル内の値に変化があれば、再レンダリングを行います。
jQueryのon()メソッドは、DOMオブジェクト以外も監視できることを最近知りました…。

// モデルを監視
$(model).on('groupIsChanged', function() {
  // テキストを再レンダリング
  $('span').trigger('render');
});

 


こちら、初期表示。

// テキストを初期表示
$('span').trigger('render');

// JavaScript処理完了後に画面表示
$('body').css('display', 'block');

 


ES6のことは一切考慮してません。笑
次回以降の記事で、ES6に書き換えてみようと思います。



以下、全コード。

<html>
<script src="jquery.js"></script>
<script>

$(function() {

  var model = (function() {

    /* privateメンバ */
    var count = 0;
    var groups = [];
    // 現在のグループを取得
    var getNowGroup = function() {
      return groups[getIndex()];
    };
    // 現在のインデックスを取得
    var getIndex =  function() {
      // 0 → 1 → 2 → 0 → 1 → 2... とインデックスをループ
      return count % groups.length
    };

    /* publicメンバ */
    // グループを追加
    var addGroup =  function(group) {
      groups.push(group);
    };
    // グループを変更
    var changeHerGroup = function() {
      count++;
      $(this).trigger('groupIsChanged');
    };
    // グループ名を表示
    var getGroupName = function () {
      return getNowGroup().getName();
    };

    // publicメンバを開示
    return {
      addGroup: addGroup,
      changeHerGroup: changeHerGroup,
      getGroupName: getGroupName
    };

  })();

  // Controller
  (function() {

    // グループ用のコンストラクタ関数
    function Group(name) {
      var name = name; // 書かなくても良い
      this.getName = function() {
        return name;
      };
    }
    // グループ追加
    model.addGroup(new Group('乃木坂46'));
    model.addGroup(new Group('欅坂46'));
    model.addGroup(new Group('SKE48'));

    // ボタンクリック時のイベントハンドラ
    $('button').on('click', function() {
      model.changeHerGroup();
    });

  })();

  // View
  (function() {

    // モデルを監視
    $(model).on('groupIsChanged', function() {
      // テキストを再レンダリング
      $('span').trigger('render');
    });

    // レンダリング用カスタムイベント
    $('span').on('render', function() {
        $(this).text(model.getGroupName());
    });

    // テキストを初期表示
    $('span').trigger('render');

    // JavaScript処理完了後に画面表示
    $('body').css('display', 'block');

  })();

});
</script>
<body style="display: none">
  <button>グループ変更</button>
  <p><span></span>の白石麻衣です。</p>
</body>
</html>

Riot.jsでモデルっぽいものを書いてみる

Riot.jsを初めて触ってみた。
あまりにシンプル過ぎて驚いた!
ほとんど覚えること無しに、それなりに書けそう。

というわけで、前回・前々回の流れで、簡単なサンプルをRiot.jsで書いてみます。

悩んだのが、Riot.jsにはモデルが見当たらない…?
おそらく、Observableがそれにあたるのか…?

とりあえず、モデルっぽいものを使ってみることにします。



HTMLはこれだけ。

<html>  
<body>
  <div id="nogi"></div>
  <div id="nogi-text"></div>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/riot/2.3.18/riot+compiler.js'></script>
</body>
</html>




モデルの発火、監視を可能にする。

<script>
var model = {
  isNogi: true
}
riot.observable(model);
</script>




これが、カスタムタグ。
HTMLタグと、JavaScriptとが合体してるイメージ。スタイルシートも定義できます。
「このタグはこういう挙動をする」ってのが見て一発で分かる。
わざわざソースコードを追いかけ回す必要もない。便利だなぁ…。

<script type='riot/tag'>  
<nogi>
  <button onclick='{ invert }'>{ name }</button>

  // モデルを更新
  this.invert = function () {
    model.isNogi = !model.isNogi;
    // 通知イベント発火
    model.trigger('hasInverted');
  };

  // 通知イベントを検知して、要素を描画
  model.on('hasInverted', (function () {
    this.render();
  }).bind(this));

  this.render = function () {
    this.name = model.isNogi ? '乃木坂46' : '欅坂46';
  };

  // 初期描画
  this.render();
</nogi>
</script>




こちら、別のカスタムタグ。
モデルの発火を検知して、再レンダリングする。

<nogi-text>
  <p>{ description }</p>

  // 通知イベントを検知して、要素を描画
  model.on('hasInverted', (function () {
    this.render();
  }).bind(this));

  this.render = function () {
    this.description = model.isNogi ? '2011年に結成されました。' : '2015年に結成されました。';
    this.update();
  };

  // 初期描画
  this.render();
</nogi-text>




マウントする。マウントって何?
カスタムタグと関連付けます。

<script>
  // マウント
  riot.mount('#nogi', 'nogi');
  riot.mount('#nogi-text', 'nogi-text');
</script>



覚えるのも書くのも簡単そうなので、せめて公式ドキュメントは全部読んでおこうと思います。
低コストで最新のJavaScriptフレームワークを身につけられる。お得ですなぁ…。