結果セット整形

Posted by muchag | Symfony 1.x | 2016-11-19 (土) 12:04:24

DQL における、結果セットの整形。
Data Hydrators

【環境】
Symfony:1.4.13
doctrine:1.2.4
書式

execute, fetchOne, fetchAll などの第2引数に指定する。

  1. $q = Doctrine_Core::getTable('User')
  2.     ->createQuery('u')
  3.     ->leftJoin('u.Email e')
  4.     ->where('u.username = ?', 'jwage');
  5.  
  6. $user = $q->fetchOne(array(), Doctrine_Core::HYDRATE_RECORD);
詳細
HYDRATE_RECORD

レコードをオブジェクトとして取得。
デフォルト値なので、省略すればこれになる。

HYDRATE_ARRAY

連想配列として取得。

HYDRATE_SCALAR

連想配列として取得。
キーが、カラム名_フィールド名 という書式になっている。

HYDRATE_SINGLE_SCALAR

配列として取得。
WHERE 句で指定したカラムの値を、
添え字 0 から始まる配列の要素に格納(しているように見える)。

HYDRATE_ON_DEMAND

公式サイトを読む限り、
「1レコードしか読み込まないのでメモリを節約できるよ」
って書いてある気がするけど
実験したら、時間制限に引っ掛かって戻ってこなかったので
今回はパス。

HYDRATE_RECORD_HIERARCHY

nested ビヘイビアを利用しているときに使える引数っぽい。
オブジェクトとして取得。

HYDRATE_ARRAY_HIERARCHY

nested ビヘイビアを利用しているときに使える引数っぽい。
連想配列として取得。

HYDRATE_NONE

配列として取得。
取得してきたデータを、添え字 0 から始まる配列の要素に格納(しているように見える)。

HYDRATE_ARRAY_SHALLOW

連想配列として取得。
SELECT で指定したカラムだけを取得。

※下2つは、下記参考サイトから拾ってきた。
公式サイトに全種類が載っていないのは不思議・・・

HYDRATE_RECORD_SHALLOW

上記定数を参考に、こんなのもあるかと思ったけどなかったw

参考サイト

stackoverflow:Select One column Doctrine DQL(2013-01-19)

Symfony 1.x | 2016-11-19 (土) 12:04:24 |

ヒアドキュメント

Posted by muchag | PHP | 2016-01-16 (土) 10:53:42

なが~い文字列を扱うための構文。

PHP Manual:文字列 ヒアドキュメント

【環境】
PHP:5.4.7
注意

WordPress なのか iG:Syntax Hiliter なのか
<<< という記述をすると、表示が乱れるので <<< と表記してある。 でも、最後のコード表記は大丈夫なのよね~・・・。

書式
  1. &lt;&lt;&lt;EOD
  2. hogehoge
  3. EOD
  4. ;
  5.  
  6. // PHP 5.3.0 以降、開始 ID をダブルクォーテーションで囲めるように。
  7. // Nowdoc との違いを明示するのが目的?
  8. &lt;&lt;&lt;"EOM"
  9. hogehoge
  10. EOM
  11. ;
ID

ヒアドキュメントを識別するためのもの。
上記例では、EOD が該当する。

ルール
  • 英数字およびアンダースコアのみ
  • 数字でない文字またはアンダースコアで始まる必要がある
  • 終端 ID がある行には、セミコロン (;) 以外の他の文字が含まれていてはならない
  • ID はインデントしてはならない
  • セミコロンの前に空白やタブを付けてはいけない
  • 終端 ID の前の最初の文字は、使用するオペレーティングシステムで定義された 改行である必要がある
  • 最後の区切り文字の後にもまた、改行を入れる必要がある
;(セミコロン)
  1. $foo = &lt;&lt;&lt;EOD
  2. hogehoge
  3. EOD;

よく上記のようなスクリプトを見かけるけど
最後の ;(セミコロン)は、あくまでも $foo = に対するものであって
ヒアドキュメントとは関係ない。

つまり、下記のように記述すればイメージしやすい。

  1. $foo = &lt;&lt;&lt;EOD
  2. hogehoge
  3. EOD
  4. ;
文字列部

変数は、そのまま記述すればパースされる。

  1. echo &lt;&lt;&lt;EOT
  2. My name is "$name". I am printing some $foo->foo.
  3. Now, I am printing some {$foo->bar[1]}.
  4. This should print a capital 'A': \x41
  5. EOT;
  6.  
  7. // 出力結果
  8. My name is "MyName". I am printing some Foo.
  9. Now, I am printing some Bar2.
  10. This should print a capital 'A': A

PHP Manual:文字列 変数のパース
これを読んでも、「簡単」と「複雑」の定義がはっきりしないので
全て {}(波括弧、中括弧)を付けるほうが安全かも?

定数や関数

定数や関数は $(ドル)から始まらないので、処理不能・・・?

最初に Google 先生に質問したら、
「定数や関数は使えない」という回答ばっかり引っかかってきたけど
2回目の質問では、アッサリと解決策が。
Blog:PHPのヒアドキュメント内で定数や関数を動かす
Web、ときどきDTP。(うぇヴ(web)屋のネタ帳。):PHPのヒアドキュメント内で関数を使う。計算もしたい。

クラス

クラスを用意して、そのメンバ変数やメソッドとして利用する。

可変変数

定数は、そのまま変数にしてしまえばよい。
関数は、可変変数を用いて処理。

利用

直接出力しても

  1. echo &lt;&lt;&lt;EOD
  2. hogehoge
  3. EOD;

変数に代入してもよい。

  1. $foo = &lt;&lt;&lt;EOD
  2. hogehoge
  3. EOD;
Nowdoc

PHP 5.3.0 以降の機能。

ヒアドキュメントはダブルクォーテーションで括られた文字列として扱われるのに対して
Nowdoc は、シングルクォーテーションで括られた文字列として扱われる。

中身について、パース処理を行わないので、エスケープをする必要がなくなる。
SGML の

書式

開始 ID を、シングルクォーテーションで括る。

  1. &lt;&lt;&lt;'EOD'
  2. hogehoge
  3. EOD
  1. echo <<<'EOT'
  2. My name is "$name". I am printing some $foo->foo.
  3. Now, I am printing some {$foo->bar[1]}.
  4. This should not print a capital 'A': \x41
  5. EOT;
  6.  
  7. // 出力結果
  8. My name is "$name". I am printing some $foo->foo.
  9. Now, I am printing some {$foo->bar[1]}.
  10. This should not print a capital 'A': \x41
参考サイト

すたら日記:PHP ヒアドキュメントの使い方:2通り

PHP | 2016-01-16 (土) 10:53:42 |

array_column

Posted by muchag | PHP | 2016-01-13 (水) 21:51:59

array_column は、連想配列群の中から、単一カラムを取り出して配列で返してくれる。

【環境】
PHP: 5.4.7
内容

レコードセットのように、同型の連想配列が連番で格納されているような配列において
各レコードから特定のカラムの値だけを抽出する。

書式

array array_column ( array $input , mixed $column_key [, mixed $index_key = null ] )

php:array_column

第1引数:元配列
第2引数:抽出したいカラム名
第3引数:抽出後キーにしたいカラム名。省略可

  1. $users = Array(
  2.     0 => Array(
  3.         'id' => 'user001',
  4.         'name' => 'tanaka',
  5.     },
  6.     1 => Array(
  7.         'id' => 'user002',
  8.         'name' => 'suzuki',
  9.     },
  10.     2 => Array(
  11.         'id' => 'user003',
  12.         'name' => 'yamada',
  13.     },
  14. );
  15.  
  16. $names = array_column($users, 'name');
  17. print_r($names);
  18.  
  19. // 出力結果
  20. Array(
  21.     0 => 'tanaka',
  22.     1 => 'suzuki',
  23.     2 => 'yamada',
  24. );
  25.  
  26. $names = array_column($users, 'name', 'id');
  27. print_r($names);
  28.  
  29. // 出力結果
  30. Array(
  31.     'user001' => 'tanaka',
  32.     'user002' => 'suzuki',
  33.     'user003' => 'yamada',
  34. );
あれ?

PHP 5.5 から新しく加わった関数。

残念!
私は 5.4.7 だった><

代替

がっかりポンだ~と思っていたら
代替というか、GitHub:array_column/src/array_column.php で関数が提供されていた。
ありがとうございます!

ファイルに保存して、インクルードパスへ配置して
require して、見事動作しました♪

PHP | 2016-01-13 (水) 21:51:59 |

Doctrine -> DISTINCT, LEFT JOIN

Posted by muchag | Symfony 1.x | 2015-12-22 (火) 16:19:36

注意書きを見つけたのでメモ。

【環境】
OpenPNE:3.8.15
Symfony:1.4.13
doctrine:1.2.4
参考サイト

Qiita:DoctrineのDISTINCTは正しく動作しない
→回避策あり

Qiita:DoctrineのLEFT JOINはまともに使えない
→回避策あり

Symfony 1.x | 2015-12-22 (火) 16:19:36 |

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘hoge’ for key ‘PRIMARY’

Posted by muchag | Symfony 1.x | 2015-11-17 (火) 17:52:00

【環境】
Symfony:1.4.13
doctrine:1.2.4
状況

既存データの更新をしようとして起きた。

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘hoge’ for key ‘PRIMARY’

ちょこっといじったら、エラー内容が下記に変わったけど、エラー内容はほぼ同じ?

SQLSTATE[23000]: Integrity constraint violation: 1557 Upholding foreign key constraints for table ‘op_category’, entry ”, key 1 would lead to a duplicate entry
原因

既存データとセーブデータの ID(プライマリーキー)が被ってるよ、という警告のまんま。

経緯

フォームを利用しているものの、Ajax 通信であったため、フォームをそのまま突っ込むのではなく
手動でデータをセットして保存しようとしていた。

Symfony(Doctrine)では、save メソッドで、INSERT と UPDATE を自動判別してくれるとのことで
INSERT のときと、同じデータのセットの仕方にしていたけど
これがいけなかったみたい。

解決

後述の通り、事前にフォームにデフォルト値を設定することで
エラーは消えた。

INSERT と UPDATE を自動判別

フォーム -> 表示(Model 有り) にあるように

  1. $sample = Doctrine::getTable('Sample')->find($request->getParameter('id'));
  2. $this->form = new MySampleForm($sample);

とすることで、フォームのデフォルト値(既存データ)がセットされ
これによって、INSERT と UPDATE を自動判別しているっぽい。

Symfony 1.x | 2015-11-17 (火) 17:52:00 |

SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

Posted by muchag | Symfony 1.x | 2015-11-17 (火) 16:12:02

【環境】
Symfony:1.4.13
doctrine:1.2.4
エラー
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

引数の数が合ってないよっと。

状況1

i18n データを取得しようとして、データ取得 の如く
find メソッドで取得しようとして起きた。

原因

i18n データを取得するのに、親テーブルへアクセスして
Translation テーブルの主キーのデータを渡していた。

  1. $category = Doctrine::getTable('Category')->find(Array($id, $culture));
解決

親テーブルの主キーは ID のみなので、下記のように変更して解決。

  1. $category = Doctrine::getTable('Category')->find($id);
状況2

DQL でサブクエリを利用しようとした。
ただ、メインクエリとサブクエリと
同一のテーブルだった。

  1. $query = Doctrine::getTable('Relation')->createQuery('r')
  2.     ->select('r.hoge_id');
  3.  
  4. $sub_query = $query->createSubquery()
  5.     ->select('sub.hoge_id')
  6.     ->from('Hoge sub')
  7.     ->where('sub.category_id = ?', 123);
  8.    
  9. $query->where("r.hoge_id IN ({$sub_query->getDql()})")
  10.     ->andWhere('r.category_id = ?', 456);
原因

テーブルエイリアスを変えてみたものの
category_id というカラムは同一と見なされ、値は2種類あるので、
数が合ってない、ということみたい。

解決

解決できてないけど、そもそもこの DQL は、本命からずれたお試しだったので
本命の DQL に戻しただけ。

直接の解決策は見つかっていない。

Symfony 1.x | 2015-11-17 (火) 16:12:02 |

Invalid hydration mode specified: ja_JP

Posted by muchag | Symfony 1.x | 2015-11-17 (火) 15:33:17

【環境】
Symfony:1.4.13
doctrine:1.2.4
状況

データ取得 の find メソッドを初めて実装したときに起きた。

Invalid hydration mode specified: ja_JP
原因

複合主キーの検索値を配列ではなく、そのまま引数に入れていたため。

  1. $category = Doctrine::getTable('Category')->find($id, $culture);
解決

配列に書きなおして無事解決。 😯

  1. $category = Doctrine::getTable('Category')->find(Array($id, $culture));
別の問題

この問題は解決したんだけど、別の問題が残っていたのは秘密
下記コードには別の間違いがありました。。。
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

Symfony 1.x | 2015-11-17 (火) 15:33:17 |

array_reduce

Posted by muchag | PHP | 2015-11-10 (火) 14:00:40

array_reduce は、配列要素を集約して単一の結果を返す関数。
階層構造の再帰処理 で参考にさせていただいたソースに使われていた。

【環境】
php:5.4.7
array_reduce

mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )

配列 array の各要素に callback 関数を繰り返し適用し、 配列を一つの値に減らします。

php:array_reduce

第1引数:当該配列
第2引数:コールバック関数。無名関数可
第3引数:戻り値の初期値。省略可。省略した場合はデフォルトが null

イメージ

配列について、foreach でグルグル回す代わりに使う。

利用例
  1. // コールバック関数。その1
  2. function sum($carry, $item)
  3. {
  4.     $carry += $item;
  5.     return $carry;
  6. }
  7.  
  8. // コールバック関数。その2
  9. function product($carry, $item)
  10. {
  11.     $carry *= $item;
  12.     return $carry;
  13. }
  14.  
  15. $a = array(1, 2, 3, 4, 5);
  16. $x = array();
  17.  
  18. var_dump(array_reduce($a, "sum")); // int(15)
  19. var_dump(array_reduce($a, "product", 10)); // int(1200), because: 10*1*2*3*4*5
  20. var_dump(array_reduce($x, "sum", "No data to reduce")); // string(17) "No data to reduce"
コールバック関数

関数名そのものを文字列にて渡す。

  1. var_dump(array_reduce($a, "sum"));
無名関数

関数名を指定するのではなく、無名関数にてその場に定義してもOK。

  1. var_dump(array_reduce($a, function sum($carry, $item) {
  2.     $carry *= $item;
  3.     return $carry;
  4.   }
  5. ));
引数

$carry:戻り値。処理の間保持される
$item:配列の各要素。foreach($array as $key => $val) の $val

PHP | 2015-11-10 (火) 14:00:40 |

SQL の確認

Posted by muchag | OpenPNE 3.x,Symfony 1.x | 2015-10-08 (木) 18:34:07

発行されている SQL 文を確認する方法。

【環境】
OpenPNE:3.8.15
Symfony:1.4.13
doctrine:1.2.4
モデル

モデル生成時に自動的に生成される Table クラスで書く場合。

  1. $result = $this->createQuery('p')
  2.     ->leftJoin('p.Translation t WITH t.lang = ?', $culture)
  3.     ->andWhere('p.id = ?', 1)
  4.     ->execute();

上記のようにすることで目的のレコードを取得できるけど
createQuery メソッドで Doctrine_Query が返ってきているので
最後の execute メソッドを getSqlQuery に変更することで、生 SQL 文を取得できる。

  1. echo $this->createQuery('p')
  2.     ->leftJoin('p.Translation t WITH t.lang = ?', $culture)
  3.     ->andWhere('p.id = ?', 1)
  4.     ->getSqlQuery();
実際に利用してみて思った
  1. $query = $this->createQuery('p')
  2.     ->leftJoin('p.Translation t WITH t.lang = ?', $culture)
  3.     ->andWhere('p.id = ?', 1);
  4.  
  5. echo $query->getSqlQuery();
  6.  
  7. $result = $query->execute();

こういう風に記述してやると見やすい。

色々な記事で、execute() を切り離して記述しているのは
こういうことがあるからかも?

OpenPNE 3.x,Symfony 1.x | 2015-10-08 (木) 18:34:07 |

階層構造の再帰処理

Posted by muchag | PHP | 2015-09-25 (金) 17:27:28

多次元配列の階層構造をリスト表示したくて Google 先生へ質問した結果。

【環境】
php:5.4.7
手法

時間がないので、参考サイトの中から採用したものをコピペ。

  1. $data = [
  2.     [
  3.         'hoge_id' => 2,
  4.         'hoge' => '親2',
  5.         'parent_id' => 0,
  6.     ],
  7.     [
  8.         'hoge_id' => 8,
  9.         'hoge' => '孫',
  10.         'parent_id' => 4,
  11.     ],
  12.     [
  13.         'hoge_id' => 3,
  14.         'hoge' => '子2-1',
  15.         'parent_id' => 2,
  16.     ],
  17.     [
  18.         'hoge_id' => 4,
  19.         'hoge' => '子7-1',
  20.         'parent_id' => 7,
  21.     ],
  22.     [
  23.         'hoge_id' => 7,
  24.         'hoge' => '親7',
  25.         'parent_id' => 0,
  26.     ],
  27.     [
  28.         'hoge_id' => 1,
  29.         'hoge' => '親1',
  30.         'parent_id' => 0,
  31.     ]
  32. ];
  33.  
  34. class HtmlUlBuilder
  35. {
  36.     private $data;
  37.  
  38.     public function __construct($data)
  39.     {
  40.         $this->data = $data;
  41.     }
  42.  
  43.     public function buildFromParent($parent_id)
  44.     {
  45.         $children = array_filter($this->data, function ($element) use ($parent_id) {
  46.             return $element['parent_id'] === $parent_id;
  47.         });
  48.  
  49.         if (count($children) === 0) return '';
  50.  
  51.         return '<ul>' . array_reduce($children, function ($current, $element) {
  52.             return $current . PHP_EOL . '<li>' . $element['hoge'] . $this->buildFromParent($element['hoge_id']) . '</li>';
  53.         }, '') . '</ul>';
  54.     }
  55. }
  56.  
  57. usort($data, function ($a, $b) {
  58.     return $a['hoge_id'] > $b['hoge_id'];
  59. });
  60.  
  61. $builder = new HtmlUlBuilder($data);
  62. $html = $builder->buildFromParent(0);
  63.  
  64. echo $html;
参考サイト

stack overflow:PHP多次元配列から階層リストタグ(略)を出力したい

PHP | 2015-09-25 (金) 17:27:28 |
次ページへ »