【jQuery】select(セレクトボックス)の入力値に合わせて、入力項目の表示・非表示を切り替える


JavaScriptを使用して、セレクトボックスの内容に合わせて、入力項目の表示・非表示の制御を行う方法。
やり方は色々ありますが、今回はアニメーションなどを利用せず、極力簡単に実装出来る方法を選択しました。

実装には jQuery の change イベントを使用し、セレクトボックスの内容に変更があった場合に、スタイルシート(css)でHTMLタグの表示・非表示の切り替えの処理を行っています。

サンプルはこちら

下記のコードでは、
・タイプが「映画」の場合は、「著者」の入力ボックスを非表示
・タイプが「」の場合は、「著者」の入力ボックスを表示
という処理を行っています。

<script type="text/javascript">
$(function(){
  $('#tr_type select[name="type"]').change(function() {
    if ($('select[name="type"] option:selected').val() == 'book') $('#tr_writer').css('display','table-row');
    else $('#tr_writer').css('display','none');
  });
});
</script>
<table>
  <tr id="tr_type">
    <th>タイプ</th>
    <td>
      <select name="type">
        <option value="movie">映画
        <option value="book">本
      </select>
    </td>
  </tr>
  <tr id="tr_title">
    <th>タイトル</th>
    <td>
      <input type="text" name="title" value="" size="20" maxlength="20">
    </td>
  </tr>
  <tr id="tr_writer" style="display: none;">
    <th>著者</th>
    <td>
      <input type="text" name="writer" value="" size="20" maxlength="20">
    </td>
  </tr>
</table>

簡単に解説を

3行目
change イベントの実行

4行目
「本」が選択されていたら、「著者」の tr のスタイルシートを「display: none;」(非表示)から「display: table-row」(表示)に変更

5行目
「本」以外が選択されていたら、「著者」の tr のスタイルシートを「display: none;」(非表示)に変更

25行目
tr にスタイルシート「style=”display: none;”」を指定することで、ページ読み込み時は非表示にする

jQuery を使うとタグの制御が本当に簡単にできますね。
こんなに短いコードで実装できるとは思いませんでした。

参考サイト
・change イベントについて > change(fn) – jQuery 日本語リファレンス
・スタイルシートの display > とほほのスタイルシート入門


MySQLのWarning(Code 1592)Unsafe statement written to the binary log…BINLOG_FORMAT = STATEMENTの回避方法


MySQLにて、カテゴリなどの階層構造のデータの持ち方を、書籍「SQL アンチパターン」を参考に「経路列挙型」で保持してみたところ、UPDATE にて下記の Warning が発生。

Note (Code 1592): Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave.

ブログ用にちょっと書き換えましたが、UPDATE 文の内容はだいたい下記のような内容。

UPDATE category SET 
  category_c_path = (
    SELECT temp_cd.category_c_path FROM (
      SELECT category_c_path FROM category WHERE category_i_id = 1
    ) as temp_cd
  ) || LAST_INSERT_ID() || "/"
WHERE category_i_id = LAST_INSERT_ID();

自分自身のテーブルからデータを取得して UPDATE をかけてるので、マスタとスレーブでデータが異なってしまう場合があるよ、ということらしい。

調べてみたところ、BINLOG_FORMAT を MIXED にすれば、危険な関数を使用した場合には ROW(実際に変更された行データを記録)モードになり、それ以外は STATEMENT(実際に実行されたSQLを記録)モードになるとのこと。

危険な関数は使用してないけど、今回のケースでも適用できるのかな?と疑問に思いつつ試してみる。

ということでまずは、SET SESSION で現在の接続だけを MIXED に変更してテスト。

SET SESSION binlog_format = 'MIXED';

# 設定が MIXED に変わっていることを確認
SHOW SESSION VARIABLES LIKE 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+

# 一応、グローバルは STATEMENT のままなのを確認
SHOW GLOBAL VARIABLES LIKE 'binlog_format';
+---------------+-----------+
| Variable_name | Value     |
+---------------+-----------+
| binlog_format | STATEMENT |
+---------------+-----------+

問題の SQL を実行してみて Warning が発生しないことを確認。
問題がなければ、グローバルの設定も変更。

SET GLOBAL binlog_format = 'MIXED';

あとは、再起動後も設定が保持されるように、my.cnf を編集。

vim /etc/my.cnf

[mysqld]
・・・
# レプリケーションフォーマット
binlog_format = MIXED

これにて設定完了。
my.cnf への記述が間違ってないか心配なら、再起動して確認をしておいた方が無難かな。

service mysqld restart
SHOW GLOBAL VARIABLES LIKE 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+

「SQLアンチパターン」はテーブル設計の仕方など、かなり参考になる良書なのですが、今回のような環境によって Warning が発生するケースがある場合は、注釈あたりで MIXED にするといいよって書いてあると、より親切だな~と思いました。

参考サイト
MySQL :: MySQL 5.1 リファレンスマニュアル :: 5.1.2.1 レプリケーション フォーマットのセッティング

MySQL – BINLOG_FORMAT 関連のエラーログ! – mk-mode BLOG


サービスを停止せずにログローテーション(logrotate)を行う


さくらVPSにて行った設定。
サーバー設定に関しては素人の域を出ておらず、今回行った設定についても改善点は多いかとは思いますが、備忘録として残しておきます。
(もしかしたら解釈が間違っているものもあるかもしれません)

logrotate の設定

vim /etc/logrotate.conf
# see "man logrotate" for details
# rotate log files weekly
#weekly
daily

# keep 4 weeks worth of backlogs
#rotate 4
rotate 7

logrotate の実行を週1(weekly)から、毎日(daily)に変更。
ログを残すのは4世代分から7世代分に変更。

HTTPデーモンのログローテーション設定

vim /etc/logrotate.d/httpd
/var/log/httpd/*log {
    missingok
    notifempty
    sharedscripts
#    delaycompress
    postrotate
#        /sbin/service httpd reload > /dev/null 2>/dev/null || true
        /sbin/service httpd graceful > /dev/null 2>/dev/null || true
    endscript
}

「delaycompress」は、設定ファイル(/etc/logrotate.conf)で圧縮をかけない設定になっているためコメントアウト。
圧縮する設定になってないと無視されますが、不必要ならコメントアウトするか削除しておかないと、他の人がみたときに「圧縮処理をかけたいのに設定ファイルを書き換え忘れている」のか「無視されるから残している」のか判断がつき辛いですからね。
で、削除するとログの容量が増えてきて圧縮が必要になった時に戻すのが面倒なので、コメントアウトにしています。

「httpd reload」となっていたところを、「httpd graceful」に変更。
これにより、稼働中のプロセスは終了することなく、再起動の処理が行われる。
logrotate 実行時に稼働中だったプロセスは、そのまま旧ファイルにログが書き込まれる。

PHPのログローテーション設定

vim /etc/logrotate.d/php
/var/log/php/php_errors.log {
    missingok
    notifempty
    delaycompress
    copytruncate
}

先ほどの「httpd」のローテートとは違い、こちらでは「copytruncate」を使用してログファイルのローテーションをさせています。
「copytruncate」では、現在のログファイルをコピーして旧世代のファイルを作成しています。
その後現在のログファイルの中身を消去することで、再起動処理を必要とすることなくログのローテーションを可能としています。
ただしこの方法では、「ファイルコピー ~ 中身の消去」の間に発生したログは、どちらのファイルにも残らないという欠点があります。

PHPのログはそこまでの精度を求めていないのと、「httpd graceful」を行うよりサーバーへの負担が軽いのかな?と思って「copytruncate」にしていますが、もしかしたら両方「httpd graceful」でやった方が良いのかもしれません。
この辺の知識がまだまだ足りてないため、現在はこの設定にしています。
もう少し詳しくなったら、また設定を変えるかもしれません。

ディレクティブ説明

missingok 指定のログファイルが実在しなかったとしてもエラーを出さずに処理続行
notifempty 元のログファイルが空ならばローテーションしない
sharedscripts ローテーションの条件に合致するログが複数あった場合に、prerotate, postrotate のスクリプトを一度だけ実行する
delaycompress 圧縮処理を、その次のローテーションの時まで遅らせる。compress も指定しないと無意味
postrotate

endscript
実際にローテーションが行われた後 (lastaction よりは前) に実行するスクリプト。
copytruncate copy の動作を行った後、内容を消去する。これはログファイルをリロードする術のないプログラムへの対処法のひとつ。

参考サイト
Stray Penguin – Linux Memo (logrotate)

デバッグ

# 全テスト
logrotate -dv /etc/logrotate.conf

# 個別にテスト
logrotate -dv /etc/logrotate.d/httpd

「-d」でデバッグモード、「v」をつけると詳細表示。

実行ログの確認

less /var/lib/logrotate.status