array_column

Posted by muchag | PHP |
初回投稿:2016-01-13 (水) 21:51:59 | 最終更新: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 して、見事動作しました♪

Posted by muchag | PHP |
初回投稿:2016-01-13 (水) 21:51:59 | 最終更新:2016-01-13 (水) 21:51:59

Doctrine -> DISTINCT, LEFT JOIN

Posted by muchag | Symfony 1.x |
初回投稿:2015-12-22 (火) 16:19:36 | 最終更新: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はまともに使えない
→回避策あり

Posted by muchag | Symfony 1.x |
初回投稿:2015-12-22 (火) 16:19:36 | 最終更新: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 | 最終更新:2015-11-17 (火) 18:03:03

【環境】
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 を自動判別しているっぽい。

Posted by muchag | Symfony 1.x |
初回投稿:2015-11-17 (火) 17:52:00 | 最終更新:2015-11-17 (火) 18:03:03

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 | 最終更新:2015-12-28 (月) 16:59:04

【環境】
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 に戻しただけ。

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

Posted by muchag | Symfony 1.x |
初回投稿:2015-11-17 (火) 16:12:02 | 最終更新:2015-12-28 (月) 16:59:04

Invalid hydration mode specified: ja_JP

Posted by muchag | Symfony 1.x |
初回投稿:2015-11-17 (火) 15:33:17 | 最終更新:2015-11-17 (火) 18:03:52

【環境】
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

Posted by muchag | Symfony 1.x |
初回投稿:2015-11-17 (火) 15:33:17 | 最終更新:2015-11-17 (火) 18:03:52

array_reduce

Posted by muchag | PHP |
初回投稿:2015-11-10 (火) 14:00:40 | 最終更新:2015-11-10 (火) 14:19:14

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

Posted by muchag | PHP |
初回投稿:2015-11-10 (火) 14:00:40 | 最終更新:2015-11-10 (火) 14:19:14

SQL の確認

Posted by muchag | OpenPNE 3.x,Symfony 1.x |
初回投稿:2015-10-08 (木) 18:34:07 | 最終更新:2015-12-22 (火) 22:41:40

発行されている 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() を切り離して記述しているのは
こういうことがあるからかも?

Posted by muchag | OpenPNE 3.x,Symfony 1.x |
初回投稿:2015-10-08 (木) 18:34:07 | 最終更新:2015-12-22 (火) 22:41:40

階層構造の再帰処理

Posted by muchag | PHP |
初回投稿:2015-09-25 (金) 17:27:28 | 最終更新:2015-10-28 (水) 14:18:43

多次元配列の階層構造をリスト表示したくて 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多次元配列から階層リストタグ(略)を出力したい

Posted by muchag | PHP |
初回投稿:2015-09-25 (金) 17:27:28 | 最終更新:2015-10-28 (水) 14:18:43

CSV ファイル出力

Posted by muchag | PHP |
初回投稿:2015-09-07 (月) 12:02:17 | 最終更新:2016-02-28 (日) 16:13:48

ずっと DB に頼ってばかりで、CSV ファイル出力って、ほとんどしてこなかったので
まとめた記事がなかった。

基本
文字コード変換

今では Web サイトは UTF-8 が主流になっているんだろうけど
Excel は、相変わらず Shift-JIS なので
文字コード変換が必要なときもあるでしょう。

  1. mb_convert_variables('SJIS-win', 'UTF-8', $string);
ファイル保存

試していないので、参考サイトのみ。

パス指定
ダウンロード

Qiita:PHPで連想配列を日本語エンコードしてCSV出力する
SCENE LIVE:【PHP】データをCSVでいい感じにエクスポートする方法

連想配列対応
  1. $fields = Array('name', 'content',);
  2.  
  3. $array = Array(
  4.     0 => Array('name' => 'yamada', 'content' => 'pitcher',),
  5.     1 => Array('name' => 'tanaka', 'content' => 'catcher',),
  6. );
  7.  
  8. /* ファイルポインタをオープン */
  9. $file = fopen("C:\Users\UserName\Downloads\test.csv", "w");
  10. fputcsv($file, $fields);
  11.  
  12. foreach( $array as $key => $assoc_row){
  13.   $numeric_row = array();
  14.   foreach ($fields as $header_name) {
  15.     $numeric_row[] = $assoc_row[$header_name];
  16.   }
  17.   fputcsv($file, $numeric_row);
  18. }
  19.  
  20. /* ファイルポインタをクローズ */
  21. fclose($file);
参考サイト

Qiita:PHPで連想配列を日本語エンコードしてCSV出力する

Posted by muchag | PHP |
初回投稿:2015-09-07 (月) 12:02:17 | 最終更新:2016-02-28 (日) 16:13:48

SoftDelete

Posted by muchag | OpenPNE 3.x,Symfony 1.x |
初回投稿:2015-09-06 (日) 17:41:37 | 最終更新:2015-12-28 (月) 9:45:02


SoftDelete:日本語版

レコードを削除する際に、本当に削除(物理削除)するのではなく
削除フラグを立てることによって、削除モードにする(論理削除)機能。

【環境】
OpenPNE:3.8.15
Symfony:1.4.13
doctrine:1.2.4
設定
ProjectConfiguration

ここまで実装方法などについて書いてきましたが、実はこれだけだと動きません。
Doctrineのデフォルトの設定では、Doctrine_Queryを実行前後で修正することが出来なくなっており、そこを設定してあげる必要があります。この設定の方法がsymfony 1.0とsymfony 1.1で変わってきます。

symfony 1.1ではProjectConfigurationクラスに設定を記述します。

アシアルブログ:Doctrineで論理削除を意識せずに扱う

ProjectConfiguration.class.php
  1. class ProjectConfiguration extends sfProjectConfiguration
  2. {
  3.   protected $plugins = array('sfDoctrinePlugin');
  4.  
  5.   public function configureDoctrine($manager)
  6.   {
  7.     $manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, true);
  8.   }
  9. }
OpnePNE3

OpenPNE3 では、こういう設定を追加する場所が用意されているっぽい。
まだ、成功体験をしていないので、本当にこれでよいかは不明。

(openpne3)\config\ProjectConfiguration.class.php
  1. public function setupProjectOpenPNEDoctrine($manager)
  2. {
  3.     // You can write your own configurations.
  4.  
  5.     // In default, OpenPNE uses foreign key.
  6.     // If you want not to use foreign key, comment out the following configuration:
  7.     // $manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_ALL ^ Doctrine::EXPORT_CONSTRAINTS);
  8.  
  9.     $manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, true);
  10. }
schema
schema.yml
  1. Category:
  2.   actAs:
  3.    - SoftDelete
設定変更

カラム名やデータ型を変更したい場合は、下記のように記述すればよいみたい。
Symfony world:Doctrine SoftDelete behavior usage
→この記事のコメント投稿内情報。未検証。

  1. Category:
  2.   actAs:
  3.     SoftDelete:
  4.       name: is_deleted
  5.       type: datetime

力技でやっている人もいるみたい。
@chisei のはてなブログ:Doctrineの論理削除

マニュアルとの差異

このように設定することで、当該テーブルへ deleted_at カラムが追加される
んだけど・・・

公式マニュアル では、delete カラムが追加される、とされている。
データ型も、公式マニュアルでは、TYNYINT とあるけど、実際は DATETIME になる。
バージョンの問題かしらね~。

データ挿入

んで、実際に試してみた。
CSV で、データを突っ込む際に、deleted_at カラムには null を指定しておいたところ
当該カラムの値は 0000-00-00 00:00:00 となった。

CSV から Null を放り込む方法

文系プログラマによるTIPSブログ:MySQLのload dataで日付型にnullをセットする
こちらの記事がビンゴ!

  1. LOAD DATA
  2.     LOCAL INFILE 'data.csv'
  3.     INTO TABLE [table_name]
  4.     FIELDS TERMINATED BY ','
  5.     ENCLOSED BY '"'
  6.     SET
  7.         deleted_at = nullif(deleted_at, '0000-00-00 00:00:00')
  8. ;

上記記事では、指定項目がもっといっぱいあったけど
ひとまず、私の今回の環境およびデータでは、上記 SQL にて、
無事に daleted_at カラムへ Null 値を放り込めた。

nullif

どうやら第2引数に、データ型に合わせたデフォルト値を設定するみたい。
なので、その他のデータ型は下記の通り。

  1. SET
  2.         datetime = nullif(datetime, '0000-00-00 00:00:00')
  3.         date = nullif(date, '0000-00-00')
  4.         time = nullif(time, '00:00:00')
データ取得
  1. $result = $this->createQuery('c')
  2.     ->execute();

データ取得を試みたところ
取得データ数は 0 。

SQL 確認

SQL の確認 のごとく、発行された SQL 文を確認すると、
ちゃんと WHERE 句が付記されている。

  1. WHERE (o.deleted_at IS NULL)

※エイリアスに p を指定しているのに、テーブル名の先頭文字 o をエイリアスにされているが気になるけど。。。

エイリアス o

これは、こちらの指定した DQL から Doctrine が SQL を生成する際に、
指定されたテーブルを順番に o1, o2, o3, … と再指定しているためだった。

その o も、恐らく、テーブル名の頭文字を取っていると思われる。
OpenPNE3 の場合、テーブル接頭辞を付けているため
全テーブルが op_ で始まるのよね。

原因

Doctrine が NULL を探しているのに対して、
データ側は ‘0000-00-00 00:00:00’ という値であるため
スルーされていた。

解決

上記 データ挿入→CSV から Null を放り込む方法 を用いて
値を Null にしてやることで、ちゃんとデータを取得できるようになった。

過去の対応策
  1. $result = $this->createQuery('p')
  2.     ->andWhere('p.deleted_at = ?', '0000-00-00 00:00:00')
  3.     ->execute();

これで一応拾ってくることはできた。

とりあえずはこれでいくしかないけど。。。意味なーい!
いつか解明しなければ。。。
(やっと解明できた)

エイリアス

ちなみにテーブルエイリアスを付けないと、上記記述でも拾ってこない。

  1. // NG
  2. $result = $this->createQuery()
  3.     ->andWhere('deleted_at = ?', '0000-00-00 00:00:00')
  4.     ->execute();

必ずエイリアスをつける必要があります。
ただしこれはSELECT限定で、DELETEのときはエイリアスがなくても動くようです。

アシアルブログ:Doctrineで論理削除を意識せずに扱う

Posted by muchag | OpenPNE 3.x,Symfony 1.x |
初回投稿:2015-09-06 (日) 17:41:37 | 最終更新:2015-12-28 (月) 9:45:02
« 前ページへ次ページへ »