fuelphpのvalidationについて

filedsetに対して定義できるものと、modelに対して定義できるものがある。

Fieldsetのvalidation

Fieldsetと1対1で紐付いたvalidationインスタンスのrunメソッド(return bool)を呼び出す。 $fieldset->validation()->run()

ruleの設定

各fieldに対して個別にadd_ruleで設定可能。

デフォルトで用意されているルールは下記サイトの下部を参照

http://fuelphp.jp/docs/1.9/classes/validation/methods.html

メッセージの設定

下記のいずれかの方法で可能

  • app/lang/ja/validation.php に書く
  • Validationインスタンスに対して設定する $fieldse->validation()->set_message(‘ルール名’, ‘メッセージ’)
  • 各fieldに設定する $fieldset->field(‘field名’)->set_message(‘ルール名’, ‘メッセージ’)

カスタムルール

validationインスタンス_find_ruleメソッド内で、 $thi->callablesに設定されているオブジェクトの prefixが _validationのメソッドを探しいく。

デフォルトではvalidationインスタンス自身が設定されているので、Fuel\Core\Validation 内のprefixが_validationのメソッドが適用できるようになってい る。

callablesはvalidationインスタンスのadd_callableで追加できるので、_validation_*** が定義されているクラスであれば何でもok(Fieldset自身とか、専用の Validation_Callable_Hoge のようなクラスを作ったりとか)

またadd_callableで設定しておかなくても、validationインスタンスのrunメソッドの第三引数に$temp_callableを渡せる。

modelのvalidationでカスタムルールをモデル内に定義できるのもこれで実現させてる。

https://github.com/fuel/orm/blob/1.9/develop/classes/observer/validation.php#L232

modelのvalidation

ミニマムのサンプル

<?php

class Model_Post extends \Orm\Model
{
    protected static $_properties = array(
        'id',
        'title' =>  [
            'validation' => [
                'required',
                'max_length' => [1],
                'contains_hoge'
            ]
        ],
    );

    protected static $_observers = array(
        'Orm\Observer_Validation' => ['events' => ['before_save']]
    );

    protected static $_table_name = 'posts';

    public function _validation_contains_hoge($val)
    {
        return (bool)preg_match('/hoge/', $val);
    }
}
  • $_propertiesvalidationに設定できる
  • _validation_*** という命名規則でmodelにカスタムバリデーションを定義できる
  • 上記の指定だと before_save 時にバリデーションが実行される
  • fail時は Orm/ValidationFailed がthrowされる
  • errorメッセージは app/lang/validation.php でruleごとに定義できる(model内では設定できないぽい)
  • 全体の表示の用のメッセージは $e->message で取得(フォーマットはConfigのvalidationで設定可能)
  • エラーの個別の取得は $e->get_fieldset() でmodelから生成したFieldsetが取得できるので、Fieldsetのインターフェース経由で行える。
    • $e->get_fieldset()→error('title')

仕組み

modelのpropertiesの設定から動的にFieldsetを生成し、Fieldsetのvalidationの機能を利用して実現させてる。

bootstrapのモーダルを複数開いたときの問題と対処法

bootstrapのモーダルは複数重ねて開いたときにいくつか細かい問題が発生する。 versionは3系。

2つめのモーダルの背景(.modal-backdrop)より1つめのモーダルが上に表示されてしまう

標準の挙動

背景(.modal-backdrop)はmodalごとに、bodyにappendされる(z-indexは.modalのz-index設定値 - 10)

z-indexのスタック文脈について詳しい記事

z-index再入門 - z-indexの仕組み | CodeGrid

問題点

モーダルの数、重なりに関係なく .modal.modal-backgroupより上に表示される。

対処法

.modal.modal-backdropのz-indexをstackされている要素を考慮するようにhackする

$(document).on('show.bs.modal', '.modal', function () {
    var zIndex = 1040 + (10 * $('.modal:visible').length);
    $(this).css('z-index', zIndex);
    setTimeout(function() {
        $('.modal-backdrop').not('.modal-stack').css('z-index', zIndex - 1).addClass('modal-stack');
    }, 0);
});

bodyスクロールの調整

標準の挙動

body.modal-openoverflow:hidden を指定することで、モーダルが開いているときの、body要素のscrollを無効化している

問題点

.modal-open はbodyに対して設定しているので、モーダルが複数ある場合は考慮されていない。 なので、2つめのモーダルを閉じたときに body.modal-open が外れてしまい、まだ開いているモーダルがあるのにbodyのスクロールが有効になってしまう。

対処法

モーダルを閉じた後のeventで、まだ開かれているモーダルがあるときは、再度 body.modal-openをつける

$(document).on('hidden.bs.modal', '.modal', function () {
    $('.modal:visible').length && $(document.body).addClass('modal-open');
});

right paddingの調整

標準の挙動

モーダルを開く際に、bodyに縦スクロールが発生している場合、.modal.modal-dialogではない)に対してスクロールバーの幅分paddingRightを指定する。 結果モーダルはスクロールバーの幅分を除いた表示領域内でセンタリングされる。

問題点

2つめのモーダルを開く際はbodyに overflow: hidden が指定されているため(body.moda-openによって)スクロールバーが発生していない判定になり、paddingRightが設定されない。 結果、2つめのモーダルが若干右にずれる形になる。

また、1つめのモーダルで縦スクロールが発生している場合、1つめのモーダルはスクロールバー幅分を除いた領域でセンタリングされるが、2つめのモーダルはスクロール幅分を考慮しない領域でセンタリングされるので、2つめが若干右にずれる。

対処法

うーむ。。

参考

stackoverflow.com

app.codegrid.net

デザインパターン入門【マルチスレッド編】備忘録

android開発を少しすることになってマルチスレッド周りの知識が残念な感じだったので、良い本だと噂の

Amazon CAPTCHA

を読んだので備忘録。

メモ

  • synchronizedがあったら何か守るべきものがあると考える
  • InterruptedException
    • throws InterruptedExceptionがついているメソッドは以下のように認識すると良い
      • 時間がかかる
      • キャンセルできる
    • Thread.interruptメソッドは直接InterruptedExceptionを投げるわけではなく、Threadをinterrupt状態にするだけ
    • Threadがwait,sleep,joinなどの待ち状態のときにinterrupt状態を確認していて、interrupt状態の場合にInterruptedExceptionを自ら投げる
    • 通常の処理を行っているときに急にInterruptedExceptionが発生することはない

パターン図

いくつかパターンをピックアップして、PlauntUMLの練習がてら図で表してみる。

続きを読む

vuexでのデータの扱いについて少し困ったので整理

目的

vuexを使っているある程度複雑なvueコンポーネント上で、定数的なものをどう扱っていいか解らなかったので、一旦考えを整理しておきたい。

現状の整理

なぜvuex?

コンポーネントの階層が4つまであって、propsだけで実現しようとすると、rootコンポーネントのpropsを最下層まで渡していくのが面倒だし、挙動や設計を少し変えようとした場合の変更箇所も増えるので大変。

vuexを使うと、storeを共有できるので、最下層のコンポーネントからも直接storeからstateを参照、更新(mutationの実行)ができるので楽。

vuexのデメリット?

コンポーネントが依存する先が増えるので、挙動を理解するのが少し大変になるのと、再利用性は低くなる。

props, data, storeそれぞれの役割を理解して使わないとカオスになる(なった。経験値が必要) 勿論ちゃんと整理して使えばかなり便利。

props, data, store

何に困ったか?

今回作っていたのはがっつりSPAという感じではなく、phpで動いているwebサービスのある一画面をSPA的に実装する形。

なので、

  • php側からjsに各値を渡す
  • 渡された値をstoreのデフォルト値として設定

という感じ。

php → js に渡したいデータとして、state的なもの以外にphp側では定数として定義されているデータも渡す必要があって、割と大事なデータで色々なコンポーネントから参照される。

window.constants のように定義すればいけるけど、グローバル変数に依存するのは何か違くない?と思い及び腰に。

storeに突っ込んでおけば、 mapStateでどのコンポーネントからでも必要があれば簡単に参照できるし、依存先もstoreだけになるかあらまあいいか。。と思いそうしたけどなんか違うよな。。と。(じっくり考える余裕がなかった反省)

考察

storeに突っ込むのは何がいけない?

storeはアプリ全体でリアクティブなフローを実現するための仕組みなので、state的なデータを保持するためのものなのでそもそも使い方が間違っている。

storeにconstants的なデータも入れてしまうと、stateとconstantsが入り乱れてしまうので、何が動的に変わる想定のデータで、何がimmutableな定数的なデータとして想定されているものか解らなくなってしまい、コードを読み解くのが困難になってしまう。 (読み解くの難しいコードはどんな状況であれ、避けるべきで良くないもの)

どうすべきだったか?

  • constants用のclassを定義する
    • 名前をちゃんと付けることで、windowに直接入れ込むより用途が明確になる
    • 各プロパティのgetterを定義することで、他にどういったデータがあるか解りやすくなる
  • php側から渡されるjsonをloadする関数 loadJson をexportして、loadしたらfreezeされる
    • phpから渡されるデータをload後にimmutableになる」という設計の意図が解りやすくなる
class HogeConstants
{
    constructor(json)
    {
        // json parse ..
    }

    get a() {
        return this.a
    }

    get b() {
        return this.b
    }
}

let instance

export function loadJson(json)
{
    instance = new HogeConstants(json)
    Object.freeze(instance) // singleton pattern https://www.sitepoint.com/javascript-design-patterns-singleton/
}

export function getHogeConstants()
{
    return instance
}

phpとの連携部分のjs部分で、 loadJson を呼び出しあげて、 あとはこのデータを使いたいコンポーネント側でimportすれば参照できるようになる。

アプリの規模によってはwindowの直接設定しても問題にならないこともある気がする。

あんまりvuexどうたらは関係なく、jsもある程度の規模になったらある程度設計を考えないと、という話?

開発の進め方の反省(少し複雑なフォーム編)

概要

既存のフォームのUIなどがあまりイケてないので、少しリッチな感じのUIに改修しようというプロジェクトで進捗がよくなかったので反省メモ。

体調があまり良くなくて頭が回らなかったというのもあったけど、体調に依存せずに安定したアウトプットを出したいのと、それだけではない気がしたので。

手こずった要素

validation

validation機能を実現できる要素が多かった

js側のvalidationの仕組みをあまり理解できていなかった。

既存のコードを残しつつ進めていたので、ごっちゃになってしまっていて、どうあるべきか?などを考えられていなかった。

vue

  • 全体の構成を決める前に、動的なフォーム部分を作り始めてしまったので、データの持ち方とかの設計があやふあや感じに
  • vue自体の経験値はある程度たまってきた感があるけど、サーバーサイドと比べるとまだまだ全然だから、直感でバーっと進める感じにはできない

整理

最初に仕様のイメージをクリアにする

プロトタイピング的なもので、チーム間では仕様がふわっとしている状況でも、

自分の頭の中での仕様のイメージをクリアにしないと設計ができず、ちぐはぐな感じになってしまうのでクリアにしておくべき。

構成要素の役割を知る

スキル的な部分。

例えばvalidationでいうと、php側のフォームを扱う機能とhtml標準の機能やjs側のライブラリとか重複してしまっている要素がある場合に、

それぞれ要素ができること、役割、得手不得手をちゃんと理解しているかつ、仕様をクリアにすれば、設計時にどういう要素をどのように使うかを決められるはず。

タスクを分割して順序を守る

例えば何かを改修するときに

  • 既存のコードを把握
  • 仕様を頭の中でクリアに
  • 技術的な構成要素を理解
  • 設計 ー実装

大まかにこういう感じにタスクを分割したときに、手順を守るべき。

簡単なものだったら適当にやってもなんとかなってしまうけど、少しでも複雑な場合は、

既存のコードを把握 をしているときに、「ああこういう感じか、じゃあここはこんな感じに手を加えちゃおう」のように、

実装 をしてしまうと、改修後の仕様をクリアにする前に細かい実装を先にしてしまってしまうことになるので、

既存のコードの仕様、設計に引きづられてしまって、ちぐはぐな感じになってしまいがち。

ササッと片付けてしまいたい誘惑にまけず、手順を守ることが大事?

あとこういうフローも習慣化していないと、「タスクを分割して作業の進め方を考える」こと自体の練度も上がっていかなそう。

CleanArchitecture備忘録(前編)

Amazon CAPTCHA

いい本だと噂になっている気がしたので読んでみた備忘録。

日本語版もあるけど、英語の勉強も兼ねて英語版を読んでみた。

一旦PART 1 Introduction から PART3 Design Principle まで。

続きを読む

ノンブロッキングI/Oがよく解っていなかったので作りながら調べてみた

モチベーション

WEBエンジニアとして活動してきてますが、技術的な棚卸しとして、普段良く使っているツールがどういったものかを少し深掘りしてみようという活動の一環。

nginx軽くて早くてお世話になっていますが、どうして軽くて早いのかよく解っておらず、ノンブロッキングI/Oという言葉もふわっと流していたので、深掘りしてみようという感じです。

ざっと色々なブログなどを見てみたのですが、いまいち消化できずにいたので、せっかくなので少し手を動かしながらやるか!ということで、少し勉強中のgolangで書いてみた。

続きを読む