関数呼び出しの複数の引数と単一の配列


24

パラメーターのセットを受け取り、SQLクエリの条件としてそれらに適用する関数があります。ただし、条件自体を含む単一の引数配列を優先しましたが:

function searchQuery($params = array()) {
    foreach($params as $param => $value) {
        switch ($param) {
            case 'name':
                $query->where('name', $value);
                break;
            case 'phone':
                $query->join('phone');
                $query->where('phone', $value);
                break;
        }
    }
}

私の同僚は、代わりにすべての引数を明示的にリストすることを好みました。

function searchQuery($name = '', $phone = '') {
    if ($name) {
        $query->where('name', $value);
    }

    if ($phone) {
        $query->join('phone');
        $query->where('phone', $value);
    }
}

彼の議論は、引数を明示的にリストすることで、神秘的な議論$paramが何であるかを調べるためにコードを掘り下げる必要がなく、関数の振る舞いがより明白になるということでした。

私の問題は、10以上のような多くの引数を扱うとき、これが非常に冗長になることでした。望ましいプラクティスはありますか?私の最悪のシナリオは、次のようなものです。

searchQuery('', '', '', '', '', '', '', '', '', '', '', '', 'search_query')


1
関数がパラメーターとして特定のキーを必要とする場合、少なくともそれらのキーはDocBlockに文書化する必要があります。そのようにすると、IDEはコードを詳しく調べることなく関連情報を表示できます。en.wikipedia.org/wiki/PHPDoc
Ilari Kajaste

2
パフォーマンスのヒント:foreach不要であるこの例では、あなたが使用できるif(!empty($params['name']))のではなく、foreachswitch
チボー

1
これで、1つのメソッドを使用できます。こちらをご覧になることをお勧めします:book.cakephp.org/2.0/en/models / ...他のメソッドを作成するため。標準の検索用に魔法のように生成し、特定の検索用にカスタムで開発することもできます。一般的に、それはモデルのユーザーのために明確なAPIを作成します。
リュックフランケン


2
上記の「パフォーマンスのヒント」に関する注意:盲目的!empty($params['name'])にパラメータのテストに使用しないでください。たとえば、文字列「0」は空になります。array_key_existsキーの確認に使用するか、またはisset気にしない場合は、を使用することをお勧めしますnull
AmadeusDrZaius

回答:


27

私の同僚は上記の例に対して正しいです。あなたの好みは簡潔かもしれませんが、それはまた読みにくく、したがって保守性が低くなります。そもそも関数をわざわざ書くのはなぜか、あなたの関数は何を「表に持ってくる」のか、という質問をしてください。彼の例では、私はPHPプログラマーではありませんが、関数の宣言に十分な詳細が含まれているので、その実装を気にする必要はありません。

より多くの引数に関する限り、それは通常コードのにおいと見なされます。通常、関数はやりすぎていますか?多数の引数が実際に必要であることがわかった場合、それらは何らかの形で関連しており、1つまたはいくつかの構造またはクラス(アドレスの行などの関連アイテムの配列でさえ)に属している可能性があります。ただし、非構造化配列を渡しても、コードの臭いに対処することはできません。


多数の引数が必要な場合、関数は基本的にゼロ個以上の引数を取り、それらの引数によって結果セットを制限します。引数自体は(別個のSQL句として)互いに関係がなく、同じ構造を持たないこともあります(1つは単純なWHEREでも、WHEREに加えていくつかのJOINが必要な場合もあります)。この特定のケースでは、依然としてコードのにおいと見なされますか?
xiankai

2
@xiankaiこの例では、where引数用に1つ、join指定子用に1つの配列パラメーターを作成します。適切な名前を付けることで、それでも自己文書化されます。
Jan Doggen

代わりにsetter / getterを使用し、引数をまったく渡さないとどうなりますか?それは悪い習慣ですか?セッター/ゲッターを使用する目的ではありませんか?
lyhong

私は、OPの設定が「読みにくい」(どのように?)で保守性が低いことに挑戦します。searchQuery( ''、 ''、 ''、 ''、 'foo'、 ''、 ''、 ''、 'bar')は、searchQuery(['q' => 'foo'、 'x' => 'bar'])多数の引数は不必要にコード臭です; たとえば、query()。また、引数の数が少ない場合でも、引数が直接渡されたときに発生する引数の順序の一貫性の欠如は、パラメーターをハードコーディングすることが悪い考えであることを示しています。上記の矛盾については、PHPの文字列および配列関数をご覧ください。
-MikeSchinkel

4

私の答えは多かれ少なかれ言語にとらわれません。

複雑なデータ構造(テーブル、レコード、ディクショナリ、オブジェクト...)で引数をグループ化する唯一の目的が、それらを全体として関数に渡すことである場合、それを避ける方がよいでしょう。これにより、無駄な複雑なレイヤーが追加され、意図がわかりにくくなります。

グループ化された引数自体が意味を持つ場合、その複雑さの層は設計全体を理解するのに役立ちます。代わりに抽象化層に名前を付けます。

多数の個別の引数または1つの大きな配列の代わりに、それぞれが相関データをグループ化する2つまたは3つの引数を使用するのが最適な設計であることがわかります。


1

あなたの場合、私はあなたの同僚の方法を好むでしょう。あなたがモデルを書いていて、私があなたのモデルを使ってそれらを開発していたら。同僚のメソッドの署名が表示され、すぐに使用できます。

一方、私はあなたの実装を通過する必要があります searchQuery関数を調べて、関数が期待するパラメーターを確認する必要があります。

searchQuery単一のテーブル内でのみ検索するように制限されている場合にのみ、あなたのアプローチを好むので、結合はありません。その場合、私の関数は次のようになります。

function searchQuery($params = array()) {
    foreach($params as $param => $value) {
        $query->where($param, $value);
    }
} 

したがって、配列の要素は、実際にはこのメソッドを持つクラスがコードで表す特定のテーブルの列名であることをすぐに知っています。


1

両方をしてください。 array_merge同僚が好むように、関数の上部に明示的なリストを許可しますが、必要に応じてパラメーターが扱いにくくなるのを防ぎます。

また、質問のコメントから@chiborgの提案を使用することを強くお勧めします-あなたが意図していることはずっと明確です。

function searchQuery($params = array()) {
    $defaults = array(
        'name' => '',
        'phone' => '',
        ....
    );
    $params = array_merge($defaults, $params);

    if(!empty($params['name'])) {
        $query->where('name', $params['name']);
    }
    if (!empty($params['phone'])) {
        $query->join('phone');
        $query->where('phone', $params['phone']);
    }
    ....
}

0

また、クエリ文字列に似た文字列を渡し、parse_strそれを使用して(PHPを使用しているようですが、他の言語ではおそらく他のソリューションが利用できるため)メソッド内の配列に処理することができます:

/**
 * Executes a search in the DB with the constraints specified in the $queryString
 * @var $queryString string The search parameters in a query string format (ie
 *      "foo=abc&bar=hello"
 * @return ResultSet the result set of performing the query
 */
function searchQuery($queryString) {
  $params = parse_str($queryString);
  if (isset($params['name'])) {
    $query->where('name', $params['name']);
  }
  if (isset($params['phone'])) {
    $query->join('phone');
    $query->where('phone', $params['phone']);
  }
  ...

  return ...;
}

そしてそれを

$result = searchQuery('name=foo&phone=555-123-456');

http_build_query連想配列から文字列への変換に使用できます(逆の変換parse_str)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.