開発メモ,主に補足by子連れ親父プログラマー

2014-02-16

BootstrapのModal使う時は data-keyboard="false" にしておく

BootstrapのModal使う時は data-keyboard="false" にしておく


http://getbootstrap.com/

まあ、同じModalでも画像を表示するとかだったらいいんだが、
大体うちらが作るようなモーダルはそのなかにformがあってなんかを入力するようなmodal だ。
入力中に日本語の変換を間違えたのでescキーを押したらモーダルが消えたんだよ!とクレームが付く。

ということで、モーダルの中身を書く部分に data-keyboard="false" を追加する。

<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-keyboard="false">

こんな感じ。
ついでなんで書いとくと、そのモーダルにAjaxで別ソースを読み込ませたい場合は以下のようにする。

$('#myModal').empty().modal('show').load("another.php");

この方がよく使うだろう。モーダルの中味を別テンプレートにできるし、フォームを動的に生成するphpも別ファイルにできる。

2014-02-15

vagrantでCentOSの環境があってそこに入れたMysqlが度々死ぬ

vagrantでCentOSの環境があってそこに入れたMysqlが度々死ぬ


まあ世の中便利になったもので、vagrantっつーもんがあって、これを入れるとCentOS+Apache+PHP+Mysqlみたいな開発環境が簡単に作れてしまう。ウインドウズでもマックでも。
こりゃーいい、ってんで早速使っているが、使っているとしばしばMysqlが死ぬ。

$ mysql -u root -p

とやってmysqlに入ろうとすると、

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111)

と出る。これは一体なんだろう。
このエラーメッセージで検索すると、だいたいが
・mysql.sockというファイルがないからです。作りましょう。
とか
・/var/lib/mysqlの所有者が違います
とかそんな回答が得られるんだが、だめである。
/var/lib/mysql/mysql.sock も存在しているし、/var/lib/mysql の所有者はmysql だ。

これはつまり、ないからだめなんじゃなくて、あるからだめなんである。

という訳で消す。

$ sudo rm /var/lib/mysql/mysql.sock

としてからvagrantを再起動。

$ vagrant halt
$ vagrant up
$ vagrant ssh
$ mysql -u root -p

治った。まあ多分、ぶちっと終了してしまうのでファイルが壊れるんだろう。

2012-09-14

mysqli(MySQL 改良版拡張モジュール)を使う場合のサンプルいろいろ

mysqli(MySQL 改良版拡張モジュール)を使う場合のサンプルいろいろ


いやあよもやね、mysql_real_escape_string とかが deprecated になる日がくるとは思わんかったね。
まずいでしょう、これは。まじで。

error_reporting(0);

にしとかないとあっという間にサーバーのログがパンクするんじゃないか・・・。

ま、それはともかく、mysqli に書き換えないといけない。
いや、そもそも mysqli でいいのか? PDO っつーのもあるぞ、って話なんだが。

俺は mysqli でいく。

まずは接続。

// データベース接続
$dbh = new mysqli("localhost", "username", "password", "db_name");
if ($dbh->connect_error) {
    die('Db Connect Error: '.$dbh->connect_errno.':'.$dbh->connect_error);
}
$dbh->set_charset('utf8');

id= で一個だけのデータをセレクトする。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id = '1';
    $sth->execute();
    $result = $sth->get_result();

    while ($row = $result->fetch_assoc()) {
        echo $row['id']."\n";
        echo $row['name']."\n";
    }
    $sth->close();
}

同じsqlをセレクトする値を変えて複数回実行する。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id_array = array('1', '2');
    foreach($id_array as $id) {
        $sth->execute();
        $result = $sth->get_result();

        while ($row = $result->fetch_assoc()) {
            foreach($row as $k => $v) {
                echo "$k $v";
            }
        }
    }
    $sth->close();
}

クエリの結果の行数を取得してなんかする。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->execute();
    $sth->store_result();
    if ($sth->num_rows == '3') {
        $sth->bind_result($id, $name);
        while ($sth->fetch()) {
            echo "$id, $name\n";
        }
        printf("Number of rows: %d.\n", $sth->num_rows);
    }
    $sth->free_result();
    $sth->close();
}


クエリでINを使う。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE id IN (?, ?)";
if (! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $list = array('1','3');
    $sth->bind_param('ii', $list[0], $list[1]);
    $sth->execute();
    $sth->store_result();
    $sth->bind_result($id, $name);
    while ($sth->fetch()) {
        echo "$id, $name\n";
    }
    $sth->free_result();
    $sth->close();
}

使えねーなこりゃ。

次、LIKE検索。

$sth = $dbh->stmt_init();
$sql = "SELECT id, name FROM users WHERE name LIKE ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $param);
    $myname = "理";
    $param = "%".$myname."%";
    $sth->execute();
    $sth->store_result();
    $sth->bind_result($id, $name);
    while ($sth->fetch()) {
        echo "$id, $name\n";
    }
    $sth->free_result();
    $sth->close();
}

一行だけ更新。

$sth = $dbh->stmt_init();
$sql = "UPDATE users SET name = ? WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myid);
    $myname = "里香";
    $myid = "3";
    $sth->execute();
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

ループで複数行更新。

$sth = $dbh->stmt_init();
$sql = "UPDATE users SET name = ? WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myid);
    $id_array = array('8'=>'佐藤', '2'=>'山田');
    foreach($id_array as $myid => $myname) {
        $sth->execute();
    }
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

削除。

$sth = $dbh->stmt_init();
$sql = "DELETE FROM users WHERE id = ?";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('s', $id);
    $id = "2";
    $sth->execute();
    printf("%d Row affected.\n", $sth->affected_rows);
    $sth->close();
}

インサートとインサートしたIDの取得。

$sth = $dbh->stmt_init();
$sql = "INSERT INTO users (name, email) VALUES (?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myemail);
    $myname = "歌代";
    $myemail = "xxxx@xxxx.xxx";
    $sth->execute();

    // INSERTED_ID の取得
    printf("%d Inserted.\n", $dbh->insert_id);// こっち
    printf("%d Inserted.\n", $sth->insert_id);
    $sth->close();
}

インサート処理でエラー処理を付ける。

$sth = $dbh->stmt_init();
$sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('iss', $myid, $myname, $myemail);
    $myid = "2";
    $myname = "歌代";
    $myemail = "xxxx@xxxx.xxx";
    if ( ! $sth->execute()) {
        die('failed'.$sth->error);
    }
    $sth->close();
}

トランザクションで失敗したらロールバック。

$dbh->autocommit(FALSE);
$sth = $dbh->stmt_init();
$sql = "INSERT INTO myusers (name, email) VALUES (?, ?)";
if ( ! $sth->prepare($sql)) {
    die('Failed to prepare statement\n');
} else {
    $sth->bind_param('ss', $myname, $myemail);
    $myname = "夢";
    $myemail = "xxxx@xxxx.xxx";
    if ( ! $sth->execute()) {
        $dbh->rollback();
    } else {
        $dbh->commit();
    }
    $sth->close();
}

うーん、、こんなとこかー。


2012-08-27

CodeIgniterで list() っつー名前のメソッド名を作るとエラーになる。

CodeIgniterで list() っつー名前のメソッド名を作るとエラーになる。

こんな感じで、

public function index()
 {
  $this->load->view('welcome_message');
 }

 public function list()
 {
  echo "ok";
 }

list() メソッドを追加すると・・・、

Parse error: syntax error, unexpected T_LIST, expecting T_STRING in /Users/xxx/Sites/codeigniter/application/controllers/welcome.php on line 25


なんでやねん!

ドラゴンクエストX 目覚めし五つの種族 オンライン(通常版)

2012-08-19

OS X Mountaion Lion で MacPorts を使って開発環境を整える(postgres編)


OS X Mountaion Lion で MacPorts を使って開発環境を整える(postgres編)


まずは、

$ sudo port install postgresql90

とすると終わり際に、

To use the postgresql server, install the postgresql90-server port

と出るので、引き続き、

$ sudo port install postgresql90-server

を実行。終わり際に、

###########################################################
# A startup item has been generated that will aid in
# starting postgresql90-server with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo port load postgresql90-server
###########################################################

と、

To create a database instance, after install do
 sudo mkdir -p /opt/local/var/db/postgresql90/defaultdb
 sudo chown postgres:postgres /opt/local/var/db/postgresql90/defaultdb
 sudo su postgres -c '/opt/local/lib/postgresql90/bin/initdb -D /opt/local/var/db/postgresql90/defaultdb'

To tweak your DBMS, consider increasing kern.sysv.shmmax by adding an increased kern.sysv.shmmax .. to /etc/sysctl.conf

が出る。
順番にやっていく。

$ sudo port load postgresql90-server
$ sudo mkdir -p /opt/local/var/db/postgresql90/defaultdb
$ sudo chown postgres:postgres /opt/local/var/db/postgresql90/defaultdb
$ sudo su postgres -c '/opt/local/lib/postgresql90/bin/initdb -D /opt/local/var/db/postgresql90/defaultdb'

ここで、だーっと出る。

The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale ja_JP.UTF-8.
The default database encoding has accordingly been set to UTF8.
initdb: could not find suitable text search configuration for locale ja_JP.UTF-8
The default text search configuration will be set to "simple".

fixing permissions on existing directory /opt/local/var/db/postgresql90/defaultdb ... ok
creating subdirectories ... ok
selecting default max_connections ... 20
selecting default shared_buffers ... 2400kB
creating configuration files ... ok
creating template1 database in /opt/local/var/db/postgresql90/defaultdb/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects' descriptions ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
loading PL/pgSQL server-side language ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the -A option the
next time you run initdb.

Success. You can now start the database server using:

    /opt/local/lib/postgresql90/bin/postgres -D /opt/local/var/db/postgresql90/defaultdb
or
    /opt/local/lib/postgresql90/bin/pg_ctl -D /opt/local/var/db/postgresql90/defaultdb -l logfile start

最後のやつをやってみる。

$ /opt/local/lib/postgresql90/bin/postgres -D /opt/local/var/db/postgresql90/defaultdb

エラーになる。

postgres cannot access the server configuration file "/opt/local/var/db/postgresql90/defaultdb/postgresql.conf": Permission denied

パーミッションの問題か。
sudo su postgres して再トライ。

$ sudo su postgres
$ /opt/local/lib/postgresql90/bin/postgres -D /opt/local/var/db/postgresql90/defaultdb
LOG:  database system was shut down at 2012-08-11 04:10:00 JST
LOG:  autovacuum launcher started
LOG:  database system is ready to accept connections

動いたっぽい。

$ /opt/local/lib/postgresql90/bin/psql -l -U postgres                              List of databases
   Name    |  Owner   | Encoding | Collation | Ctype |   Access privileges  
-----------+----------+----------+-----------+-------+-----------------------
 postgres  | postgres | UTF8     | C         | C     |
 template0 | postgres | UTF8     | C         | C     | =c/postgres          +
           |          |          |           |       | postgres=CTc/postgres
 template1 | postgres | UTF8     | C         | C     | =c/postgres          +
           |          |          |           |       | postgres=CTc/postgres
(3 rows)

よさそうだ。
パスを通しておく。

$ vim .profile

export PATH=/opt/local/lib/postgresql90/bin:$PATH

テスト用DBを作っておく。

$ createdb sample_db -U postgres -E UTF-8

psqlして、

$ psql sample_db -U postgres

テーブルを作る。

sample_db=# CREATE TABLE users (
sample_db(# id SERIAL PRIMARY KEY,
sample_db(# name varchar(255)
sample_db(# );
NOTICE:  CREATE TABLE will create implicit sequence "users_id_seq" for serial column "users.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users"
CREATE TABLE

確認。

sample_db=# \d
              List of relations
 Schema |     Name     |   Type   |  Owner  
--------+--------------+----------+----------
 public | users        | table    | postgres
 public | users_id_seq | sequence | postgres
(2 rows)

sample_db=# \d users
                                 Table "public.users"
 Column |          Type          |                     Modifiers                     
--------+------------------------+----------------------------------------------------
 id     | integer                | not null default nextval('users_id_seq'::regclass)
 name   | character varying(255) |
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)

データを入れておく。

sample_db=# INSERT INTO users (name) VALUES ('yamada');
INSERT 0 1

セレクトして確認。

sample_db=# select * from users;
 id |  name 
----+--------
  1 | yamada
(1 row)

再びportでインストール

$ sudo port install php5-postgresql

アパッチ再起動

$ sudo /opt/local/apache2/bin/apachectl restart

infophp()で確認。

簡単なphpを書いて接続テスト。

<?php
$conn = pg_pconnect("dbname=sample_db user=postgres");
if (!$conn) {
  echo "An error occured.\n";
  exit;
}

$result = pg_query($conn, "SELECT * FROM users");
if (!$result) {
  echo "An error occured.\n";
  exit;
}

while ($row = pg_fetch_row($result)) {
  echo "Id: $row[0]  Name: $row[1]";
  echo "<br />\n";
}

表示される。

Id: 1 Name: yamada

よさそうだ。

2012-08-18

OS X Mountaion Lion で MacPorts を使って開発環境を整える(perl編)

OS X Mountaion Lion で MacPorts を使って開発環境を整える(perl編)


使うことはないかもしれないが、使えないのも困るだろうということで、perlの設定もしておく。
まず、

$ sudo vim /opt/local/apache2/conf/httpd.conf

として、

<Directory "/opt/local/apache2/cgi-bin">
     AllowOverride None
     Options None
     Order allow,deny
     Allow from all
</Directory>

Options None のところを、コメントアウトして、
Options Indexes MultiViews ExecCGI FollowSymLinks Includes
に変更

<Directory "/opt/local/apache2/cgi-bin">
     AllowOverride None
     Options Indexes MultiViews ExecCGI FollowSymLinks Includes
    #Options None
     Order allow,deny
     Allow from all
</Directory>

あと、

AddHandler cgi-script .cgi

のコメントアウトを外す。

アパッチ再起動

$ sudo /opt/local/apache2/bin/apachectl restart

で、

#!/opt/local/bin/perl
print "Content-type: text/html\n\n";
print "ok";

とだけ書いたファイルを test.cgi として保存して、htdocs 以下に置く。
パーミッションの設定、

$ chmod 0755 test.cgi

をやっておく。
あとはブラウザから開ければOK。



2012-08-17

OS X Mountaion Lion で MacPorts を使って開発環境を整える(MySQL編)

OS X Mountaion Lion で MacPorts を使って開発環境を整える(MySQL編)


引き続きMySQLのインストールへ。
まずは、

$ sudo port install mysql5-server

終わり際に、こんなメッセージと、

###########################################################
# A startup item has been generated that will aid in
# starting mysql5-server with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo port load mysql5-server
###########################################################

こんなメッセージが出る。

If this is a new install, in order to setup the database you might want to run:
sudo -u _mysql mysql_install_db5

悩ましいね。
とりあえず、

$ sudo port load mysql5-server

なにも起きない。
続いて、

$ sudo -u _mysql mysql_install_db5

と入れてみると、

Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/opt/local/lib/mysql5/bin/mysqladmin -u root password 'new-password'
/opt/local/lib/mysql5/bin/mysqladmin -u root -h bohr.local password 'new-password'

Alternatively you can run:
/opt/local/lib/mysql5/bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /opt/local ; /opt/local/lib/mysql5/bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /opt/local/mysql-test ; perl mysql-test-run.pl

Please report any problems with the /opt/local/lib/mysql5/bin/mysqlbug script!

だーっと何か出た。
さっぱりわからんのでとりあえず、

$ /opt/local/lib/mysql5/bin/mysql_secure_installation

をやってみる。

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!


In order to log into MySQL to secure it, we'll need the current
password for the root user.  If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/opt/local/var/run/mysql5/mysqld.sock' (2)

現在のrootのパスワードをいれろってメッセージが出るが設定してないじゃん。
設定してないから何もいれなくてもいいのか?
が、空でenterしても反応なし。
これは何かオカシイ。
なんか間違ってる。
ということでmac本体を再起動だ!
・・・・・
で、もう一回ここから。

$ /opt/local/lib/mysql5/bin/mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!


In order to log into MySQL to secure it, we'll need the current
password for the root user.  If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
Enter current password for root (enter for none):
OK, successfully used password, moving on...

やっぱり、ただエンターキーだけ打てばいいってことらしい。
進む。

Setting the root password ensures that nobody can log into the MySQL
root user without the proper authorisation.

Set root password? [Y/n] y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
 ... Success!

引き続きrootのパスワード設定になったので、2回入れる。
成功!

By default, a MySQL installation has an anonymous user, allowing anyone
to log into MySQL without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] y
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] y
 ... Success!

By default, MySQL comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] y
 ... Success!

Cleaning up...



All done!  If you've completed all of the above steps, your MySQL
installation should now be secure.

Thanks for using MySQL!

あとは、なんじゃようわからんが全部yesで完了!
いやあ、こんな設定初めてやったかも。

$ /opt/local/lib/mysql5/bin/mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 5.1.63 Source distribution

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

オッケーだ。
パスが通っていないので、

$ vim ~/.profile

して、

export PATH=/opt/local/lib/mysql5/bin:$PATH

を追加。

次へ。

$ sudo port install php5-mysql

終わり際に、以下のメッセージが出るので、

To use mysqlnd with a local MySQL server, edit /opt/local/etc/php5/php.ini and set
mysql.default_socket, mysqli.default_socket and pdo_mysql.default_socket
to /opt/local/var/run/mysql5/mysqld.sock

php.ini を編集する。

$ sudo vim /opt/local/etc/php5/php.ini

で、default_socket の空になっている = に書きこむ。
PDO。

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/pdo_mysql.default-socket
pdo_mysql.default_socket = /opt/local/var/run/mysql5/mysqld.sock

mysql。

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/mysql.default-socket
mysql.default_socket = /opt/local/var/run/mysql5/mysqld.sock

mysqli。

; Default socket name for local MySQL connects.  If empty, uses the built-in
; MySQL defaults.
; http://php.net/mysqli.default-socket
mysqli.default_socket = /opt/local/var/run/mysql5/mysqld.sock

アパッチ再起動。

$ sudo /opt/local/apache2/bin/apachectl restart

infophp()でmysqlが入ってることを確認。
テスト用データベースを作成する。

mysql> CREATE DATABASE sample_db DEFAULT CHARACTER SET utf8;
Query OK, 1 row affected (0.00 sec)

ユーザー権限を設定。

mysql> GRANT ALL ON sample_db.* TO sample_user@localhost IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.00 sec)

データベースを指定して、

mysql> use sample_db
Database changed

テーブルを作成。

mysql> CREATE TABLE users (
    -> id INT AUTO_INCREMENT,
    -> name varchar(255),
    -> PRIMARY KEY  (id)
    -> );
Query OK, 0 rows affected (0.04 sec)

テーブルを確認。

mysql> show tables;
+---------------------+
| Tables_in_sample_db |
+---------------------+
| users               |
+---------------------+
1 row in set (0.00 sec)

一個データをインサート。

mysql> INSERT INTO users (name) VALUES ('yamada');
Query OK, 1 row affected (0.00 sec)

セレクトして確認。

mysql> select * from users;
+----+--------+
| id | name   |
+----+--------+
|  1 | yamada |
+----+--------+
1 row in set (0.00 sec)

DBに接続してセレクトする簡単なphpを書く。
php.netからそのまま。

<?php
$link = mysqli_connect('localhost', 'sample_user', 'password', 'sample_db');
if (!$link) {
    die('Connect Error (' . mysqli_connect_errno() . ') '
            . mysqli_connect_error());
}
echo 'Success... ' . mysqli_get_host_info($link) . "\n";

if ($result = mysqli_query($link, "SELECT name FROM users")) {
    printf("Select returned %d rows.\n", mysqli_num_rows($result));

    /* 結果セットを開放します */
    mysqli_free_result($result);
}

mysqli_close($link);

画面に表示される。

Success... Localhost via UNIX socket Select returned 1 rows.

よさそうだ。


2012-08-10

OS X Mountaion Lion で MacPorts を使って開発環境を整える(apache, PHP編)

OS X Mountaion Lion で MacPorts を使って開発環境を整える(apache, PHP編)


という訳で無事 Mountaion Lion のクリーンインストールも完了し、開発環境を再構築しようってことで四苦八苦。
まずはMacPortsのこのページ


http://www.macports.org/install.php


から Mountaion Lion のリンクをクリックしてダウンロード。
MacPorts をインストール。
それから Apple の開発者ページ


https://developer.apple.com/downloads/index.action


へ行って Command Line Tools (OS X Mountain Lion) for Xcode をダウンロードしてインストール。
(実際にはここでログインが必要で、なんだっけな、iCloudのアカウントで入ってメールアドレスのVerifyがあって、そんでやっとダウンロードできる)


そんで、まずはアパッチ。


$ sudo port install apache2

終わり際に


###########################################################
# A startup item has been generated that will aid in
# starting apache2 with launchd. It is disabled
# by default. Execute the following command to start it,
# and to cause it to launch at startup:
#
# sudo port load apache2
###########################################################

とかって出るので、以下を実行。


$ sudo port load apache2
$ port installed

として確認してみると、アパッチと一緒に


openssl @1.0.1c_0 (active)
 perl5 @5.12.4_0+perl5_12 (active)
 perl5.12 @5.12.4_1 (active)
 sqlite3 @3.7.13_0 (active)

この辺もインストールされているのが分かる。
で、次はphp。


$ sudo port install php5 +apache2 +pear

こちらも終わり間際で


To customize php, copy
/opt/local/etc/php5/php.ini-development (if this is a development server) or
/opt/local/etc/php5/php.ini-production (if this is a production server) to
/opt/local/etc/php5/php.ini and then make changes.

と表示されるので、


$ sudo cp /opt/local/etc/php5/php.ini-development /opt/local/etc/php5/php.ini

とする。

ついでにphp.iniを編集して、


$ sudo vim /opt/local/etc/php5/php.ini

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = Asia/Tokyo 

とタイムゾーンの設定を入れておく。
引き続き、apacheのhttpd.conf を編集。


$ sudo vim /opt/local/apache2/conf/httpd.conf

として、


# Virtual hosts
Include conf/extra/httpd-vhosts.conf

この部分のコメントアウトを外す。
さらに、


AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

となっているあたりに以下を追加。


AddType application/x-httpd-php .php

引き続き


$ sudo vim /opt/local/apache2/conf/extra/httpd-vhosts.conf

を編集。下の方を全部消して、以下のように変更


<VirtualHost *:80>
   DocumentRoot "/opt/local/apache2/htdocs"
   ServerName localhost
 </VirtualHost>
          
 <VirtualHost *:80>
     DocumentRoot "/Users/xxxx/htdocs"
     ServerName vhost
     <Directory "/Users/xxxx/htdocs">
       order deny,allow
       allow from All
       Options All
       AllowOverride All
     </Directory>
</VirtualHost>

自分のユーザーフォルダーの直下にhtdocsを作っておく。


$ mkdir /Users/xxxx/htdocs

その中に、適当な index.html を入れておく。
ついでに


<?php
phpinfo();

とだけ書いてあるinfo.phpファイルを作って入れておく。

あと、


$ sudo vim /private/etc/hosts

として


127.0.0.1   vhost

を追加。


$ sudo /opt/local/apache2/bin/apachectl restart

でアパッチ再起動。ブラウザで見てみる。


http://vhost/
http://vhost/info.php

OKなら mbstring とかも入れる。


$ sudo port install php5-mbstring
$ sudo /opt/local/apache2/bin/apachectl restart

とかやって。phpinfo() でちゃんと反映されているか確認する。
ここまでで一週間くらいかかった。(笑)
先は長いって。



2012-07-08

一つ目を選択したら二つ目のセレクトの値が変化するようにしたいんだが、ソースの文字コードがEUCでjqueryのload()を使って読み込むと文字化けしてしまう。


一つ目を選択したら二つ目のセレクトの値が変化するようにしたいんだが、ソースの文字コードがEUCでjqueryのload()を使って読み込むと文字化けしてしまう。



文字コードがEUCなのは昔のソースなのでしょうがない。
いまさら直しようがない。
jquery の load() だと化けるってのもしょうがないだろう。調べるだけ無駄だ。
ということで、php側で一旦データを全部読み込んで、それをJSON形式のデータにして、直接javascriptのスクリプト部分に埋め込むことにしよう。

データのイメージ:
一つ目のセレクトがこんな感じであって、選択すると、

<select name="category_id">
    <option value="0">爬虫類</option>
    <option value="1">鳥類</option>
    <option value="2">昆虫option>
</select>
こんなようなデータが読み込まれて二つ目のセレクトが生成される。(鳥類だったら"1"のデータがセレクトになる)

var per_array ='{
    "0":{
        "1":"トカゲ",
        "2":"ワニ",
        "3":"ザリガニ",
    },
    "1":{
        "4":"スズメ",
        "5":"ハト",
        "6":"トンボ"
    },
    "2":{
        "7":"コオロギ",
        "8":"バッタ"
    }
}';

データベースにはこんな感じで入っている。

SELECT * FROM ikimono;
 id | category_id |   name   
----+-------------+----------
  1 |           0 | トカゲ
  2 |           0 | ワニ
  3 |           0 | ザリガニ
  4 |           1 | スズメ
  5 |           1 | ハト
  6 |           1 | トンボ
  7 |           2 | コオロギ
  8 |           2 | バッタ
(8 rows)

まず、データベースから値を読み込んで、JSON形式のデータを作成する。

$query = "SELECT id, name, category_id FROM ikimono";
$result = pg_query($pg, $query);
$n = pg_numrows($result);
if ($n){
    for ($i=0;$i<$n;$i++){
        $id = pg_result($result, $i, 0);
        $name = pg_result($result, $i, 1);
        $category_id = pg_result($result, $i, 2);
        // ここで一個一個配列に入れておいて
        $select_array[$category_id] .= '"'.$id.'":"'.$name.'",';
    }
}
// ここでカテゴリー別に {} で囲む
foreach($select_array as $k => $v) {
    $v = preg_replace('/,$/', '', $v);
    $ikimono_data .= '"' . $k . '":{' . $v . '},';
}
$ikimono_data = preg_replace('/,$/', '', $ikimono_data);// 最後のカンマを削除

で、ページ表示部分のjavascript部分をこんな感じで。
配列の定義のところに直接phpでデータを埋め込む。


<script>
$(document).ready(function(){
    $('select[name=category_id]').change(function(){
        var category_id = $(this).val();
        var ikimono_array ='{<?php echo $ikimono_data; ?>}';
        var obj = jQuery.parseJSON(ikimono_array)[category_id];
        var ikimono_select = '';
        for (key in obj) {
            ikimono_select += '<option value="'+key+'">' + obj[key] + "</option>";
        }
        $('select[name=ikimono_id]').empty().append(ikimono_select);
    }).change();
});
</script>

二個目のセレクトは空っぽで用意しておく。

<select name="ikimono_id">
</select>

もっと他にいい方法がありそうな予感が大いににするが、とりあえずこんなところで。

2012-07-01

登録日と更新日のフィールドがあって、その中で一番新しい日付を一発でセレクトするクエリー(CASE文とMAX)

登録日と更新日のフィールドがあって、その中で一番新しい日付を一発でセレクトするクエリー(CASE文とMAX)

登録日が created で、更新日が modified で、データを新規登録した時にね、
created には insert するけど、modified にはしない。
本当に編集した時だけ modified に入れるようなプログラムになってたとするじゃないですか。

そうするとデータはこんな感じになる訳で。

          created           |          modified          
----------------------------+----------------------------
 2009-03-17 10:18:24.792449 | 2009-03-17 10:32:55.900294
 2009-03-25 19:32:18.205694 | 
 2009-03-26 14:44:18.787866 | 
 2009-03-30 16:56:56.128813 | 
 2012-06-22 18:26:11.540513 | 
 2012-06-23 12:18:09.649044 | 
 2012-06-22 18:26:37.815316 | 2012-06-26 15:07:45.34223
 2012-06-21 12:46:03.151537 | 2012-06-26 16:44:05.811683
 2012-06-26 19:05:44.473284 | 2012-06-26 20:15:18.196717
 

modified は NULL になってるデータがある訳です。

こういう状態で、更新があった一番新しい日付をセレクトせよ、ってことなんですが。。。


 SELECT MAX(CASE WHEN modified IS NULL THEN created ELSE modified END) AS dates FROM items;
 

これでこうなる。

           dates            
----------------------------
 2012-06-26 20:15:18.196717
(1 row)
まあ、最初っから両方入れとけよって話ですね。



2012-04-04

XAMPP for Windows の .htaccess 設置でエラーで Symfony2 用に short_open_tag が Offにできない

XAMPP for Windows の .htaccess 設置でエラーで Symfony2 用に short_open_tag が Offにできない

いやー、まあ、何事かと思いましたけどね。 XAMPP のデフォルトでは、php.ini の short_open_tag が On で、apache の AllowOverride が All じゃない、 ってことで、Symfony2 を動かそうとして short_open_tag を Off にするとメタメタになってしまうわけです。 で、悩むこと数日間。

設定は、

C:\xampp\apache\conf\httpd.conf
ではなく、
C:\xampp\apache\conf\extra\httpd-xampp.conf 
でやります。

以下のようになっている部分があるので、

<Directory "C:/xampp/htdocs/xampp">
    <IfModule php5_module>
     <Files "status.php">
      php_admin_flag safe_mode off
     </Files>
    </IfModule>
    AllowOverride AuthConfig
</Directory>
以下のように書き換えます。
<Directory "C:/xampp/htdocs/xampp">
    <IfModule php5_module>
     <Files "status.php">
      php_admin_flag safe_mode off
     </Files>
    </IfModule>
    AllowOverride All
</Directory>

それから、C:\xampp\php\php.ini を開いて、

short_open_tag = Off
として、 C:\xampp\htdocs\xampp 以下に、.htaccess を置いて、
php_flag short_open_tag On 
と書けばよい。ちなみに Windows で .htaccess というファイルを作るには、FTPソフトを使えばよい。

2012-01-13

OSX の MacPorts で ZendFramework を入れてみた

OSX の MacPorts で ZendFramework を入れてみた

まずは、インストール。 検索すると、

port search zend
こうでるので、
ZendFramework @1.11.11 (www, lang)
    A framework for developing PHP web applications
こうする。

sudo port install ZendFramework

インストールの最後の方でこんなメッセージが出る。

If this is your first install, you might want to add  /opt/local/www/ZendFramework/library to the
include_path in your php.ini  (e.g. include_path = ".:/opt/local/www/ZendFramework/library"). 

If you want to use the extra ZendX components  you might want to add
/opt/local/www/ZendFramework/extras/library too  (e.g. include_path =
".:/opt/local/www/ZendFramework/library:/opt/local/www/ZendFramework/extra/library").

ということで、php.ini を編集。

cd /opt/local/etc/php5
sudo vim php.ini

として、include_path の部分を、

include_path = ".:/opt/local/lib/php:/opt/local/www/ZendFramework/library:/opt/local/www/ZendFramework/extra/library"
とする。アパッチ再起動。

sudo /opt/local/apache2/bin/apachectl restart

で、zendのコマンドラインツールであるzf.shが使えるようにパスを通しておく。

vim ~/.profile

でエディターを起動して、以下を追加。

export PATH=/opt/local/www/ZendFramework/bin:$PATH

アプリを作りたいディレクトリへ行って、

zf create project sample
と打つ。

こんな画面が表示された。

2011-12-26

OSX の MacPorts で入れた mysql-connector-java は一体どうやって使えばいいのか?

OSX の MacPorts で入れた mysql-connector-java は一体どうやって使えばいいのか?

引き続き java の servlet でのデータベース接続をテストしていた時のことである。
tomcat も MacPorts で入れたんだから Connector/J も当然 MacPortsで 入れるべきだろう、ということで、

sudo port install mysql-connector-java
こうした訳ですが、これはどうやら以下の場所に設置されるようです。

/opt/local/share/java/mysql-connector-java-5.0.jar

で、こいつをどう使ったらいいのか?
まずはコンパイルする時のために自分の.profile にパスを追加。
エディターで、

vim ~/.profile  
として、以下を追加。

export CLASSPATH=/opt/local/share/java/tomcat6/lib/servlet-api.jar
export CLASSPATH=/opt/local/share/java/mysql-connector-java-5.0.jar:$CLASSPATH
export CLASSPATH=$CLASSPATH:.

パスが通ったかどうか、

echo $CLASSPATH
して確認。

これでコンパイルは通る。

で、tomcat上で、作ったservletを実行するためには、

/opt/local/share/java/mysql-connector-java-5.0.jar
このファイルを、
/opt/local/share/java/tomcat6/webapps/sample/WEB-INF/lib
以下に置く(sampleは自分で作ったservlet置き場)。

もしくは、

/opt/local/share/java/tomcat6/lib/
以下に置く。

シンボリックリンクでもいけるので、

cd /opt/local/share/java/tomcat6/lib
sudo ln -s /opt/local/share/java/mysql-connector-java-5.0.jar mysql-connector-java-5.0.jar
こうしておけばよいかもしれない。

2011-11-20

OSX Lion に Tomcat6 を入れて Java のサーブレットで HelloWorld するまでのすったもんだ

OSX Lion に Tomcat6 を入れて Java のサーブレットで HelloWorld するまでのすったもんだ

そういえばLionにしてから自宅の開発環境がほったらかしになってたな、と、嫌な予感はしていたんだが、いざ始めてみると大仕事に。 Java の SDK は入ってるはずだから Tomcat だけ入れればいいはず、と思って、

sudo port install tomcat6

と打った最初っからつまづいた。 まず MacPorts そのものがバージョン2.0.3にアップしていてこの selfupdate ができない。 Xcode を4.1にバージョンアップしろ、と言われる。 でApp Store で Xcode を検索してインストール。 App Store の画面でインストール済みになって、それからどうすればいいのか分からず、途中作業中断もはさんで大幅に時間ロス。 ふと見ると Application の中に Install Xcode というアイコンができているのでそれを起動で無事完了。 MacPorts のバージョンアップをして、引き続き Tomcat を入れる。

http://localhost:8080

を開いてみると得体の知れない EnterpriseDB とかいう画面が開くだけなので、(←なんすかね、これ)

sudo vim /opt/local/share/java/tomcat6/conf/server.xml

として、8080になってるところを8090に変更。 さらに、

sudo vim /opt/local/share/java/tomcat6/conf/tomcat-users.xml

として、Tomcat のアプリケーションマネージャ画面に入るためのIDとパスワードを設定する。

<tomcat-users>
    <role rolename="manager-gui"/>
    <user username="myname" password="mypassword" roles="manager-gui"/>
</tomcat-users>

HelloWorld 用のjava ファイルを用意して、

javac HelloWorld.java 

とするも文字化け。

vim ~/.profile

として、末尾に

alias javac="javac -J-Dfile.encoding=UTF8"
alias java="java -Dfile.encoding=UTF8"

を追加。文字化けは収まったが、コンパイルに失敗するので、 さらに .profile に

export CLASSPATH=/opt/local/share/java/tomcat6/lib/servlet-api.jar

を追加。

javac HelloWorld.java 

とやって、HelloWorld.class を作成する。

/opt/local/share/java/tomcat6/webapps

以下に、

/opt/local/share/java/tomcat6/webapps/sample

というフォルダーを作り、さらにその中に、

/opt/local/share/java/tomcat6/webapps/sample/WEB-INF
/opt/local/share/java/tomcat6/webapps/sample/WEB-INF/classes
/opt/local/share/java/tomcat6/webapps/sample/WEB-INF/src
/opt/local/share/java/tomcat6/webapps/sample/WEB-INF/lib

とフォルダーを作って、 classes の中に HelloWorld.class を、src の中に HelloWorld.java を、 WEB-INF 直下に web.xml を置いて、

http://localhost:8090/sample/Hello

で完成。

2011-11-13

JavaScript の継承とカプセル化

JavaScript の継承とカプセル化

参考文献:Supercharged JavaScript Graphics: with HTML5 canvas, jQuery, and More より。

いろんなやり方があるが、これが一番自然な方法なんじゃないかと筆者は言っている。

<script type="text/javascript">
var pet = function(name, legs) {
    var that = {
        name: name,
        getDetails: function(){
            return that.name + ' has ' + legs + ' legs';
        }
    };
    return that;
};
var cat = function(name) {
    var that = pet(name, 4);
    that.action = function(){
        return 'free as a bird';
    };
    return that;
};
</script>
var neko = cat('tama');

こうして neko を作った後で、neko の name を変えることはできるが、legs は変えられない、という訳である。

2011-11-06

CakePHP1.3で作る会員管理システム(26) バッチ処理

バッチ処理

では最後、バッチを使ってデータを更新したり、削除したりします。 バッチの処理は /app/venders/shells/ の中にファイルを作ります。今回はmembersテーブルのデータを処理するので、 /app/venders/shells/member.php とします。 で、中に以下のように書きます。

<?php
class MemberShell extends Shell {
    var $uses = array('Member');
    function main() {

        //メールアドレスにgを含むデータを削除、関連テーブルからも
        $this->Member->deleteAll(
            array('Member.email LIKE' => "%g%"), true, false
        );
    }
}
?>

$uses で使うモデルを指定しています。 削除ではなく、更新をする場合は、

$this->Member->updateAll(
    array('Member.birthday' => "'1970-03-15'"),
    array('Member.created <=' => "$this_day")
);

こんな感じになります。 このファイルを用意しておいて、コンソールから、

cd /Users/myname/Sites/cake/app
cake member

と実行します。app に入っていることが大事です。 crontab から実行する時はフルパスで書けばよいでしょう。

/Users/myname/Sites/cake/cake/console/cake member -app /Users/myname/Sites/cake/app

以上で今回作ったシステムのまとめは終わりです。

[おわり]

CakePHP1.3で作る会員管理システム(25) 検索結果CSVダウンロード

検索結果CSVダウンロード

検索結果をCSVでダウンロードする処理を追加します。 検索の処理部分は全く同じで言い訳ですから、同じadmin_serchアクションに、またモードを指定して、ダウンロードモードの時はCSVにするようにします。 まず、レイアウトとビューを用意しておきます。 /app/views/layouts/csv.ctp を新規作成して、

<?php
    header('content-type: text/plain');
    header("Content-Type: application/octet-stream");
    header("Content-Disposition: attachment; filename=member.csv");
?>
<?php echo $content_for_layout; ?>

とします。 ビューは、 /app/views/members/admin_download.ctpとして、

<?php foreach ($members as $member): ?>
<?php 
    echo $member['Member']['id'];
    echo $member['Member']['email'];
    echo $member['Member']['password'];
    echo $member['Type']['name'];
    echo $this->Time->format($format = 'Y/m/d', $member['Member']['birthday']);
    echo $this->Time->format($format = 'Y/m/d', $member['Member']['created']); 
?> 
<?php endforeach; ?>

このように書いておきます。 で、会員検索のビュー、admin_search.ctp に以下を追加しておきます。

<p><?php echo $this->Paginator->link(__('CSVダウンロード', true), array('mode'=>'DL')); ?></p>

こうするとリンクに引数を渡せます。 で、コントローラ members_controller.php のadmin_searchアクションの一番下に、

        if ((isset($this->passedArgs['mode'])) && ($this->passedArgs['mode'] == 'DL')) {
            Configure::write('debug', 0);
            $this->layout = 'csv';
            $data = $this->Member->find('all',  array(
                'order'=>array('Member.id'),
                'conditions' => $conditions
            ));
            $this->set("members", $data);
            $this->render('admin_download');
        }

を追加します。 mode=”DL” で来た時は $conditions だけ引き継いで検索し直してる感じですかね。

CakePHP1.3で作る会員管理システム(24) 検索とPaginationとHABTM

検索とPaginationとHABTM

で、さらにやっかいなのが、ここに「好きな物」のチェックボックスの複数選択検索を入れる場合です。 HABTMの関連テーブルですね。 一体どうすりゃいいんでしょうか。ということで相当悩みました。 結論としては、一旦その「好きな物」を選択している会員のIDを全部取り出して、そいつを条件に入れちゃえばいいんじゃなかろうかと。 クエリーのイメージはこんな感じ。

SELECT m.id FROM members m WHERE m.id IN
(SELECT member_id FROM members_favorites mf WHERE mf.favorite_id IN (1,2));

つまり()内のSELECTの部分をあらかじめデータ取得しておけばいいと。 ということでこうなった。

            $fid = $this->Member->MembersFavorite->find('list', array(
                'order'=>array('MembersFavorite.member_id DESC'),
                'group'=>array('MembersFavorite.member_id'),
                'fields' => array('MembersFavorite.member_id'),
                'conditions' => array('MembersFavorite.favorite_id' => $favorite_id)
            ));
            $conditions = array("Member.id" => $fid);
            $data = $this->paginate('Member', $conditions);

これは上記ではIN ()の中はSELECT文になっているが、CakePHPの文法だとそれは入らないようなので、実際に配列が入る。こんな感じになる。

SELECT `MembersFavorite`.`id`, `MembersFavorite`.`member_id` 
FROM `members_favorites` AS `MembersFavorite` 
WHERE `MembersFavorite`.`favorite_id` IN (1, 2, 3) 
GROUP BY `MembersFavorite`.`member_id` 
ORDER BY `MembersFavorite`.`member_id` DESC 

最終的にこうなりました。

    function admin_search() {
        $this->Member->recursive = 0;

        if (!empty($this->data)) {
            $f = $this->data['Member']['from'];
            $t = $this->data['Member']['to'];
            $email = $this->data['Member']['email'];
            $type_id = $this->data['Member']['type_id'];
            $favorite_id = $this->data['Member']['favorites'];
        }else{
            foreach ($this->passedArgs as $k => $v){
                if ($k == 'from'){
                    list($f['year'], $f['month'], $f['day']) = preg_split("/-/", $v);
                }elseif($k == 'to'){
                    list($t['year'], $t['month'], $t['day']) = preg_split("/-/", $v);
                }elseif($k == 'email'){
                    $email = urldecode($v);
                }elseif($k == 'type_id'){
                    $type_id = urldecode($v);
                } elseif(preg_match("/^favorite_id_([0-9]+)$/", $k, $regs)) {
                    $favorite_id[$regs[1]] = $v;
                }
            }
        }
     
        if (isset($f) && isset($t)){
            if ($this->_from_to_check($f, $t)) {
                $from = $f['year']."-".$f['month']."-".$f['day'];
                $to = $t['year']."-".$t['month']."-".$t['day'];
                $this->data['Member']['from'] = $f;
                $this->data['Member']['to'] = $t;
            }
        }
        if(isset($email)){
            $this->data['Member']['email'] = $email;
        }
        if(isset($type_id)){
            $this->data['Member']['type_id'] = $type_id;
        }
        if (isset($favorite_id)){
            $this->data['Member']['favorites'] = $favorite_id;
        }
        
     
        $searchword = array();
        $conditions = array();
        if (isset($from) && isset($to)){
            $searchword = array(
                "from" => urlencode($from),
                "to" => urlencode($to),
            );
            $conditions = array("Member.created BETWEEN ? AND ?" => array($from,$to));
        }
        if (isset($email) && $email){
            $searchword = $searchword + array(
                "email" => urlencode("$email"),
            );
            $conditions = $conditions + array("Member.email LIKE" => "%$email%");
        }
        if (isset($type_id) && $type_id){
            $searchword = $searchword + array(
                "type_id" => urlencode("$type_id"),
            );
            $conditions = $conditions + array("Member.type_id" => $type_id);
        }
        if (isset($favorite_id) && $favorite_id){
            foreach ($favorite_id as $k => $v){
                $temp_favotite['favorite_id_'.$k] = $v;
            }
            $searchword = $searchword + $temp_favotite;
            $fid = $this->Member->MembersFavorite->find('list', array(
                'order'=>array('MembersFavorite.member_id DESC'),
                'group'=>array('MembersFavorite.member_id'),
                'fields' => array('MembersFavorite.member_id'),
                'conditions' => array('MembersFavorite.favorite_id' => $favorite_id)
            ));
            $conditions = $conditions + array("Member.id" => $fid);
        }
     
        $data = $this->paginate('Member', $conditions);
        $this->set('searchword', $searchword);
        $this->set("members", $data);
         
        $favorites = $this->Member->Favorite->find('list');
        $types = $this->Member->Type->find('list');
        $this->set(compact('favorites','types'));
    }

2011-10-28

CakePHP1.3で作る会員管理システム(23) 検索とPagination

検索とPagination

で、検索しつつ Pagination する部分ですが、ちょっとややこしくて、一気に全部書くと訳が分からないので、仮にメールアドレスだけで検索する場合を最初に考えます。
普通に「検索」ボタンで検索した場合は

$this->data['Member']['email']

に値が渡ってきます。
一方 paginate で次へ、とかページ数をクリックした場合は、

$this->passedArgs['email']

に値が渡ってきます。
これをふまえて考えると、
まず、ボタンかリンクかで判別して渡ってきた値を変数に入れます。

if (!empty($this->data)) {
    $email = $this->data['Member']['email'];
} else {
    $email = urldecode($this->passedArgs['email']);
}

ボタンできた場合はいいですが、リンクで来た場合は$this->data['Member']に値が入っていないので、入れ直します。

if (isset($email)) {
    $this->data['Member']['email'] = $email;
}

前回ビューで作った、peginator に検索キーワードを渡すためのオプションに値を設定します。

$searchword = array(
    "email" => urlencode("$email"),
);

検索のクエリーの条件をセットします。

$conditions = array("Member.email LIKE" => "%$email%");

条件を使って検索してビューに渡します。

$data = $this->paginate('Member', $conditions);
$this->set('searchword', $searchword);
$this->set("members", $data);

基本的にはこの流れでいきます。
やっかいなのは日付データです。年月日を分割したり、結合したり、しないといけません。
ボタンから来た時は年月日の配列になってるので、そのまま入れて、リンクから来た時は文字列になってるのでsplitしたものを配列に入れます。

if (!empty($this->data)) {
    $f = $this->data['Member']['from'];
    $t = $this->data['Member']['to'];
} else {
    list($f['year'], $f['month'], $f['day']) = preg_split("/-/", $this->passedArgs['from']);
    list($t['year'], $t['month'], $t['day']) = preg_split("/-/", $this->passedArgs['to']);
}

同じように $this->data に戻しておきます。

$this->data['Member']['from'] = $f;
$this->data['Member']['to'] = $t;

conditions や searchword 用には結合したものを使います。

$from = $f['year']."-".$f['month']."-".$f['day'];
$to = $t['year']."-".$t['month']."-".$t['day'];

こんな感じになります。

$searchword = array(
    "from" => urlencode("$from"),
    "to" => urlencode("$to"),
);
$conditions = array("Member.created BETWEEN ? AND ?" => array($from,$to));

で、存在しない日付とかを入れると思いっきりエラーになるので、日付データのチェックを別途 function を作って入れておきます。
こんな感じで。

    function _from_to_check($f, $t) {
        if (!preg_match("/^[0-9]{4}$/", $f['year'])) {return false;}
        if (!preg_match("/^[0-9]{2}$/", $f['month'])) {return false;}
        if (!preg_match("/^[0-9]{2}$/", $f['day'])) {return false;}
        if (!preg_match("/^[0-9]{4}$/", $t['year'])) {return false;}
        if (!preg_match("/^[0-9]{2}$/", $t['month'])) {return false;}
        if (!preg_match("/^[0-9]{2}$/", $t['day'])) {return false;}
        if (!checkdate($f['month'], $f['day'], $f['year'])) {return false;}
        if (!checkdate($t['month'], $t['day'], $t['year'])) {return false;}
         
        return true;
    }

ここまでを整理するとこうなります。

    function admin_search() {
        $this->Member->recursive = 0;

        if (!empty($this->data)) {
            $f = $this->data['Member']['from'];
            $t = $this->data['Member']['to'];
            $email = $this->data['Member']['email'];
            $type_id = $this->data['Member']['type_id'];
        }else{
            foreach ($this->passedArgs as $k => $v){
                if ($k == 'from'){
                    list($f['year'], $f['month'], $f['day']) = preg_split("/-/", $v);
                }elseif($k == 'to'){
                    list($t['year'], $t['month'], $t['day']) = preg_split("/-/", $v);
                }elseif($k == 'email'){
                    $email = urldecode($v);
                }elseif($k == 'type_id'){
                    $type_id = urldecode($v);
                }
            }
        }
     
        if (isset($f) && isset($t)){
            if ($this->_from_to_check($f, $t)) {
                $from = $f['year']."-".$f['month']."-".$f['day'];
                $to = $t['year']."-".$t['month']."-".$t['day'];
                $this->data['Member']['from'] = $f;
                $this->data['Member']['to'] = $t;
            }
        }
        if(isset($email)){
            $this->data['Member']['email'] = $email;
        }
        if(isset($type_id)){
            $this->data['Member']['type_id'] = $type_id;
        }
     
        $searchword = array();
        $conditions = array();
        if (isset($from) && isset($to)){
            $searchword = array(
                "from" => urlencode($from),
                "to" => urlencode($to),
            );
            $conditions = array("Member.created BETWEEN ? AND ?" => array($from,$to));
        }
        if (isset($email) && $email){
            $searchword = $searchword + array(
                "email" => urlencode("$email"),
            );
            $conditions = $conditions + array("Member.email LIKE" => "%$email%");
        }
        if (isset($type_id) && $type_id){
            $searchword = $searchword + array(
                "type_id" => urlencode("$type_id"),
            );
            $conditions = $conditions + array("Member.type_id" => "$type_id");
        }
     
        $data = $this->paginate('Member', $conditions);
        $this->set('searchword', $searchword);
        $this->set("members", $data);
         
        $favorites = $this->Member->Favorite->find('list');
        $types = $this->Member->Type->find('list');
        $this->set(compact('favorites','types'));
    }

CakePHP1.3で作る会員管理システム(22) 会員一覧と会員検索

会員一覧と会員検索

会員管理を作ります。
最初に、いまのままだと見づらいので、一覧(indexアクション)の整形をしておきます。
admin_index.ctp を開いて、不要な列を削除します。
今は、

    <table cellpadding="0" cellspacing="0">
    <tr>
            <th><?php echo $this->Paginator->sort('id');?></th>
            <th><?php echo $this->Paginator->sort('email');?></th>
            <th><?php echo $this->Paginator->sort('password');?></th>
            <th><?php echo $this->Paginator->sort('type_id');?></th>
            <th><?php echo $this->Paginator->sort('birthday');?></th>
            <th><?php echo $this->Paginator->sort('img1');?></th>
            <th><?php echo $this->Paginator->sort('img2');?></th>
            <th><?php echo $this->Paginator->sort('created');?></th>
            <th><?php echo $this->Paginator->sort('modified');?></th>
            <th class="actions"><?php __('Actions');?></th>
    </tr>

こうなっていますが、password img1 img2 modified あたりはなくてもよいと思います。
(クライアント次第ですが)
対応する td の方も削除しておきます。

と、一見して、登録日(created)がDBの日付データそのままで出力されていて見づらいです。
ということで、 members_controller.php の helper にさらに ‘Time’ を追加します。

class MembersController extends AppController {

    var $name = 'Members';
    var $helpers = array('Javascript', 'Time');

で、ビューに戻り、誕生日と登録日は、
こうなっているのを

        <td><?php echo $member['Member']['birthday']; ?> </td>
        <td><?php echo $member['Member']['created']; ?> </td>

こうします。

        <td><?php echo $this->Time->format($format='Y/m/d', $member['Member']['birthday']); ?> </td>
        <td><?php echo $this->Time->format($format='Y/m/d', $member['Member']['created']); ?> </td>

ついでに、

<div class="actions">
...
</div>

の中のアクションリストは不要なので丸ごと削除しておきます。
最後に paginate の設定を入れておきます。
members_controller.php の上の方に、

    var $paginate = array(
        'limit' => 15,
        'order' => array(
            'Member.id' => 'desc'
        ),
    );

を入れます。15件で改ページ、デフォルトの並び順を会員のID降順にします。
引き続き、会員検索のページの準備をします。
一覧の admin_index.ctp にそのまま検索フォームをつけてしまうというのもありかとは思いますが、とりあえず今回は別ビュー、別アクションにします。
admin_index.ctp を別の名前で保存で admin_serch.ctp を作ります。
で、フォーム開始を

    <?php echo $this->Form->create(array("action" => "search", "type" => "post")); ?>

として、

    <table>
        <tr>
            <th>入会日</th>
            <td>
                <?php echo $this->Form->year("from", '2011','2012'); ?>年
                <?php echo $this->Form->month("from",null,array('monthNames'=>false)); ?>月
                <?php echo $this->Form->day("from",null); ?>日
                ~
                <?php echo $this->Form->year("to", '2011','2012'); ?>年
                <?php echo $this->Form->month("to",null,array('monthNames'=>false)); ?>月
                <?php echo $this->Form->day("to",null); ?>日
            </td>
        </tr>
        <tr>
            <th>メールアドレス</th>
            <td>
            <?php echo $this->Form->text("email"); ?>
            </td>
        </tr>
        <tr>
            <th>種別</th>
            <td>
            <?php echo $this->Form->select("type_id", $types); ?>
            </td>
        </tr>
        <tr>
            <th>好きな物</th>
            <td>
            <?php echo $this->Form->input('favorites', array('multiple'=>'checkbox', 'label' => false)); ?>
            </td>
        </tr>
    </table>
     
    <?php echo $this->Form->end('        検索        '); ?>

このような検索フォーム部分を追加します。
さらにその下に

    <?php $this->Paginator->options(array('url' => $searchword  )); ?>

peginator のオプションを入れておきます。
これは peginator に検索キーワードを渡すためのものです。
とりあえず、members_controller.php に以下のアクションと追加しておきます。

    function admin_search() {
        $this->Member->recursive = 0;
        $this->set('members', $this->paginate());
        $types = $this->Member->Type->find('list');
        $favorites = $this->Member->Favorite->find('list');
        $this->set(compact('types', 'favorites'));
    }

このブログを検索

Powered by Blogger.

ラベル

php (17) jQuery (13) OSX (10) MySQL (8) Javascript (7) Postgres (7) port (7) apache (6) Java (3) Smarty (2) html (2) pear (2) FCKEditor (1) XAMPP (1) css (1) git (1) perl (1) ruby (1)

Facebookバナー