- 投稿日:2020-08-09T20:59:16+09:00
Template Methodパターンを使うメリットとPHP実装例
Template Methodパターンとは
Template Methodパターンは、一連の処理の大まかなアルゴリズムをあらかじめ定義しておき、具体的な実装はサブクラスに任せることを目的としたデザインパターンです。フレームワークを構築するための手段としてよく利用されるそうです。
最初は普通の抽象メソッドとの違いがよくわかりませんでしたが、実装例を見て疑問が解消しました。
下記のdisplay()がTemplate Methodと呼ばれるものです。
abstract class AbstractDisplay { /** * オープン => 5回プリント => クローズ * という大まかな流れだけが決まっている */ public function display(): void { $this->open(); for ($i = 0; $i < 5; $i++) { $this->print(); } $this->close(); } public abstract function open(): void; public abstract function print(): void; public abstract function close(): void; }このようにアルゴリズムの大枠だけを抽象クラスで定義しておき、具体的な実装はサブクラスに委ねるのがTemplate Methodパターンです。このAbstractDisplayのサブクラスは、
open()
、print()
、close()
という3つのメソッドを実装しなければなりません。これをサブクラスの責任(Subclass Responsibility)というそうです。サブクラスの実装例
Template Methodを利用したサブクラスの実装の具体例です。
さきほどの抽象クラスAbstractDisplayを継承して、サブクラスCharDisplayとStringDisplayを実装します。
CharDisplay.php
class CharDisplay extends AbstractDisplay { private $char; public function __construct(string $char) { $this->char = $char; } public function open(): void { echo '<<'; } public function print(): void { echo $this->char; } public function close(): void { echo '>>' . "\n"; } }StringDisplay.php
class StringDisplay extends AbstractDisplay { private $string; private $width; public function __construct(string $string) { $this->string = $string; $this->width = mb_strlen($string); } public function open(): void { echo $this->printLine(); } public function print(): void { echo '|' . $this->string . '|' . "\n"; } public function close(): void { echo $this->printLine(); } private function printLine(): void { echo '+' . str_repeat('-', $this->width) . '+' . "\n"; } }2つのサブクラスは、どちらもAbstractDisplayクラスを継承しています。
「オープン => 5回プリント => クローズ」という流れは共通ですが、具体的な実装はサブクラスによって違います。実際にそれぞれのサブクラスをインスタンス化して、display()を実行した結果は次のようになります。
function main() { $charDisplay = new CharDisplay('H'); $charDisplay->display(); $stringDisplay = new StringDisplay('Hello, World'); $stringDisplay->display(); }-> % php TemplateMethod/Main.php <<HHHHH>> +------------+ |Hello, World| |Hello, World| |Hello, World| |Hello, World| |Hello, World| +------------+アルゴリズムを一元管理することができる
Template Methodパターンを利用する利点は、アルゴリズムを共通化できることだそうです。
似たような処理を複数のクラスに実装しなければならない場合、Template Methodを定義することで、アルゴリズムの共通部分を一箇所にまとめることができます。それにより、何度も似たようなロジックを記述する手間が省けるだけでなく、アルゴリズムに変更を加えたい場合も修正箇所が一箇所で済むようになります。
Template Methodパターンのメリット
Template Methodパターンは、一連の処理のおおまかなアルゴリズムをあらかじめ決めておき、具体的な実装はサブクラスに委ねるデザインパターンでした。テンプレートメソッドの中に、インスタンス生成のためのメソッドを含むものはFactory Methodパターンと呼ばれます。このように応用の幅が効くデザインパターンのようです。
Template Methodパターンのメリットは次の通りです。
- 共通するアルゴリズムを使い回すことができる
- サブクラスの実装が簡潔になる
- アルゴリズムを修正する際はテンプレートメソッドの修正だけで済む
Template Methodパターンの実装例はこちらです。
<?php abstract class AbstractDisplay { /** * オープン => 5回プリント => クローズ * という大まかな流れだけは決まっている */ public function display(): void { $this->open(); for ($i = 0; $i < 5; $i++) { $this->print(); } $this->close(); } public abstract function open(): void; public abstract function print(): void; public abstract function close(): void; } class CharDisplay extends AbstractDisplay { private $char; public function __construct(string $char) { $this->char = $char; } public function open(): void { echo '<<'; } public function print(): void { echo $this->char; } public function close(): void { echo '>>' . "\n"; } } class StringDisplay extends AbstractDisplay { private $string; private $width; public function __construct(string $string) { $this->string = $string; $this->width = mb_strlen($string); } public function open(): void { echo $this->printLine(); } public function print(): void { echo '|' . $this->string . '|' . "\n"; } public function close(): void { echo $this->printLine(); } private function printLine(): void { echo '+' . str_repeat('-', $this->width) . '+' . "\n"; } } function main() { $charDisplay = new CharDisplay('H'); $charDisplay->display(); $stringDisplay = new StringDisplay('Hello, World'); $stringDisplay->display(); } main();
- 投稿日:2020-08-09T19:23:21+09:00
Laravelのpluck()でunicodeが表示される。
問題
formに初期値を入れたくて、LaravelのEloquentで取得した行から
該当するカラムを取得する際に、unicodeでエンコードされた文字列が出力される。controller.php$content = Model::where('id', $id)->first()->pluck('content'); return view('admin.event_chat') ->with('content', $content)view.blade.php<form action="/Admin/eventChatSend" method="POST"> @csrf @if (isset($content)) <input type="text" name="content" value="{{ $content }}"> @else <input type="text" name="content" placeholder="定型文はありません"> @endif <input type="submit" value="送信"> </form>このように実装した場合、unicodeでエンコードされた文字列
content
が挿入されます。解決案
まぁ、pluck()を通すとunicodeでエンコードされるということは
当たり前のようにpluck()を使わなければいいのでView側で取ってくるか$content = $row->content
で取得すればいいです。前者で実装してみました。controller.php$row = Model::where('id', $id)->first(); return view('admin.event_chat') ->with('row', $row)view.blade.php<form action="/Admin/eventChatSend" method="POST"> @csrf @if (isset($row)) <input type="text" name="content" value="{{ $row->content }}"> @else <input type="text" name="content" placeholder="定型文はありません"> @endif <input type="submit" value="送信"> </form>
$row
がある時は必ず、content
がある程での実装となっています。まとめ
Laravelでカラムを取得するpluck()を通すとunicodeでエンコードされてしまうので
$row->content
で回避するか、View川で取得すると
utf8でエンコードされた文字列が返ってきます。
どなたかpluck()
で文字列をutf8でエンコードする方法をご存知の方はコメントお願いします!!
- 投稿日:2020-08-09T18:37:11+09:00
Docker(LEMP環境)にMailHogを追加したときのメモ
開発開始時からすでに用意していたDockerコンテナ環境で、急遽メール認証機能を実装するためメールの動作確認できるようにMailHogを設定した。結構手こずったが、多角的に調査して仮説を検証。メンターさんの力も借りてなんとか完了。
MailHogとは
メールのテスト確認ができるツール。例えば、メール送信の動作をした場合実環境にはメールは送られず、MailHogがメールを受信する。ブラウザでメール送信のプログラムの動作ができているかが容易にできる。
https://blog.mailtrap.io/mailhog-explained/
ツリー構造
今回の作業に主に関係あるのは、直下にあるdocker-compose-yml、docker/phpディレクトリにあるDockerfile。またphpの設定確認用に直下に
phpinfo();
を記述したinfo.phpを置いておく。├── app ├── css ├── docker │ ├── php │ └── web ├──docker-compose.yml ├──info.php ├── js └── view導入に必要なこと
・MailHogのメールサーバー用コンテナを作成する
・mhsendmailへのパスを通す(php側での設定。appコンテナとMailHogコンテナの橋渡し役のようなもの)元の環境
docker-compose.ymlversion: '3' services: web: image: nginx:1.15.6 ports: - "8000:80" depends_on: - app volumes: - ./docker/web/default.conf:/etc/nginx/conf.d/default.conf - .:/var/www/html app: build: ./docker/php volumes: - .:/var/www/html depends_on: - mysql mysql: image: mysql:5.7 environment: MYSQL_DATABASE: myDB MYSQL_USER: Ryo MYSQL_PASSWORD: ryo3110 MYSQL_ROOT_PASSWORD: alexi3110 ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql volumes: mysql-data:///Dockerfile/// FROM php:7.2-fpm RUN cd /usr/bin && curl -s http://getcomposer.org/installer \n\ | php && ln -s /usr/bin/composer.phar /usr/bin/composer RUN apt-get update \ && apt-get install -y \ git \ zip \ unzip \ vim RUN apt-get update \ && apt-get install -y libpq-dev \ && docker-php-ext-install pdo_mysql pdo_pgsql mysqli WORKDIR /var/www/html1
追加後の環境
docker-compose.ymlversion: '3' services: web: image: nginx:1.15.6 ports: - "8000:80" depends_on: - app volumes: - ./docker/web/default.conf:/etc/nginx/conf.d/default.conf - .:/var/www/html app: build: ./docker/php volumes: - .:/var/www/html - ./docker/php/php.ini:/usr/local/etc/php/php.ini depends_on: - mysql mysql: image: mysql:5.7 environment: MYSQL_DATABASE: myDB MYSQL_USER: Ryo MYSQL_PASSWORD: ryo3110 MYSQL_ROOT_PASSWORD: alexi3110 ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql mailhog: image: mailhog/mailhog ports: - 1025:1025 - 8025:8025 volumes: mysql-data:///Dockerfile/// FROM php:7.2-fpm RUN cd /usr/bin && curl -s http://getcomposer.org/installer \n\ | php && ln -s /usr/bin/composer.phar /usr/bin/composer RUN apt-get update \ && apt-get install -y \ git \ zip \ unzip \ vim RUN apt-get update \ && apt-get install -y libpq-dev \ && docker-php-ext-install pdo_mysql pdo_pgsql mysqli WORKDIR /var/www/html1 RUN curl -sSLO https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 \ && chmod +x mhsendmail_linux_amd64 \ && mv mhsendmail_linux_amd64 /usr/local/bin/mhsendmail \ && echo 'sendmail_path = "/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025"' > /usr/local/etc/php/conf.d/sendmail.ini変更箇所について
docker-compose.ymlmailhog: image: mailhog/mailhog ports: - 1025:1025 - 8025:8025mailhogイメージを使って、ポートを1025,8025を設定とし、メールサーバー用のコンテナを作成。
///Dockerfile//// RUN curl -sSLO https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 \ && chmod +x mhsendmail_linux_amd64 \ && mv mhsendmail_linux_amd64 /usr/local/bin/mhsendmail \ && echo 'sendmail_path = "/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025"' > /usr/local/etc/php/conf.d/sendmail.ini公式ドキュメントにある通り、sendmailの代わりにmhsendmailをphp用のコンテナにインストールする。
内容としては、mhsendmailダウンロード→ダウンロードしたmhsendmailを指定ディレクトリに移動させる→php側のメール設定としてsendmail_pathへパスを通す、といった感じ。コンテナの再ビルド
準備できたらコンテナを再構築する。すでにコンテナ運用しており、Dockerfileの内容を変更した場合、以下のコマンドでコンテナのキャッシュをしておく。忘れるとうまく変更が反映されない。
docker-compose build --no-cacheその後、普段どおりにupする
docker-compose up -dコンテナ再構築できたら
ブラウザでlocalhost:8000/info.phpでphpの設定を確認。sendmail_pathが
/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025
となっていればOK。また、localhost:8025でmailhogの画面が表示されることを確認。
送信テスト
適当にメール送信のプログラムで確認。
$result
でtrueが返れば送信できているはず。mailtest.php<?php $to = 'user@gmail.com'; $subject = '本登録をお願いします。'; $message = 'URLから本登録をお願いします。'; $from = 'test@gmail.com'; $header = "From: ".$from."\r\n"; mb_language('Japanese'); mb_internal_encoding("UTF-8"); $result = mb_send_mail($to,$subject,$message,$header); var_dump($result); exit;こんな感じでブラウザで表示。どうも日本語は文字化けしてしまうようであり、対処方法はいまのところない模様。まあテスト用なのでとりあえずOKとしておく。
ちなみに、コンテナ停止するとメールデータは削除されるが、docker-compose.ymlにvolumesでマウントする記述すれば、コンテナ停止してもメールデータが削除されないようにできる模様。しかしあくまでテスト用の運用のため、残ってほしくないので今回はスルー。
- 投稿日:2020-08-09T17:03:44+09:00
execでSQLが実行できない→半角スペースが原因だった(PHP+MySQL)
execでSQLが実行できない
PHPの勉強をしていてデータベースには接続されているのにSQLが実行できないという不具合がありました。
原因は半角スペース!
【間違ったソースコード】
<?php try{ $db = new PDO('mysql: dbname=mydb; host=127.0.0.1; charset=utf8', 'root', ''); }catch(PDOException $e){ print('DB接続エラー:'.$e->getMessage()); } $count = $db->exec('INSERT INTO .........'); print($count.'件数のデータを挿入しました');SQLが実行できたソースコード
<?php try{ $db = new PDO('mysql:dbname=mydb; host=127.0.0.1; charset=utf8', 'root', ''); }catch(PDOException $e){ print('DB接続エラー:'.$e->getMessage()); } $count = $db->exec('INSERT INTO .........'); print($count.'件数のデータを挿入しました');お気付きになりましたでしょうか?
mysql:の後の半角スペースが原因でした。
プログラミングを学び始めて約半年。
未だに見やすさのために半角スペースを入れても良い場所とそうではない場所との区別があやふやなことに気付きました。半角スペースを入れても良い場所などのルールについて学べるサイトなどありましたらぜひ教えてください。
- 投稿日:2020-08-09T11:22:09+09:00
phpとPostgreSQLでGoogle Homeを音楽プレイヤー(アルバム再生版)
始めに
前作phpでgoogle homeを簡易音楽プレイヤーではphpで書く1スクリプトでGoogle homeで音楽を再生してみようというコンセプトで作ってみましたが、1曲単発再生のみという音楽プレイヤーとしては致命的な仕様でした。Google Homeの音楽再生機能にはリクエストキューの機能は無いため、アルバムの複数曲再生には、スクリプト側でリクエストを待つ機能が必要となります。phpだけで作るのは面倒なことがわかり、crontabを使用して再生コマンドを予約する方式をとることにしました。また、楽曲データがitunesから取得できることが分かったので、PostgreSQLを使ってデータを管理してみることにしました。夏休みのプログラミングで趣味でポチポチ書いたものなので、簡単なコードですので、よろしかったら読んでみてください。
参考サイト・文献
Google Homeにプッシュ発話をさせる、Raspberry Pi 3へのgoogle-home-notifierの最新導入手順
プログラミング PHP 第3版 O'REILLY オライリー・ジャパン
SQL 第2版 ゼロから始めるデータベース操作
目次
- 前提環境
- セットアップ
- あとがき
前提環境
- Raspberry Pi 2,3 または 4
- Ubuntu または Raspberry pi OS
- Google Home
- itunesに保存されている楽曲ファイル(DRMがないもの)
セットアップ
前作phpでgoogle homeを簡易音楽プレイヤーとほぼ同じ環境となるので、この記事のセットアップに従い、php、google home notifier、itunesの音楽ファイルの設定を行ってください。phpのファイルパスはUbuntu、Raspberry pi OS共通になります。
今回ファイル数が多いため、コードの掲載はせずに、githubに公開する方式としました。
適当な場所にcloneしてください。$ git clone https://github.com/yoshiki9636/player.git $ cd playercheckdb.phpにいくつか設定する項目があります。
checkdb.php$dbport = 5432; // PostgreSQLのポート デフォルトでは 5432 $dbuser = 'XXXXX'; // PostgreSQLのユーザ名 $dbpasswd = 'XXXXX'; // PostgreSQLのパスワード $dbname = 'trackdb'; // PostgreSQLのデータベース名 今回は trackdb $ip_address = 'XX.XX.XX.XX'; // 本機のIPアドレス $ghnport = 8080; // google home notifierのポート デフォルト 8080 : :このままでは外から見えないため、/var/wwww/html/にコピーします。
$ cd .. $ sudo cp -r player /var/www/html/次に前回との差分であるデータベースの設定を行います。
まずPostgreSQLのインストールを行います。$ sudo apt install postgresqlPostgreSQLの管理ユーザは'postgres'なので、ユーザをスイッチしてPostgreSQLのコンソールを起動します。
$ sudo -u postgres -i $ psql postgres@ubuntu:/home/pi$ psql psql (12.2 (Ubuntu 12.2-4)) Type "help" for help. postgres=#通常使うユーザ名でロールを作成します。以下では、ユーザ名'pi'、パスワード'hoge'でロールを作成しています。このユーザ名、パスワードは先ほどのcheckdb.phpで設定するユーザ名およびパスワードとなります。あと、CREATEDBの権限を与えます。
postgres=# CREATE ROLE pi WITH CREATEDB login password 'hoge'; CREATE ROLE postgres=# \du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- pi | Create DB | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}次に上記ロールでデータベースを作ります。ここではロールは'pi'です。
postgres=# CREATE DATABASE trackdb WITH OWNER pi; CREATE DATABASE postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+-------------+-------------+----------------------- postgres | postgres | UTF8 | ja_JP.UTF-8 | ja_JP.UTF-8 | template0 | postgres | UTF8 | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres + | | | | | postgres=CTc/postgres trackdb | pi | UTF8 | ja_JP.UTF-8 | ja_JP.UTF-8 | (4 rows) postgres=#\q $ exit $ユーザpostgresで行うことは終了したので、\qで抜けて、exitで元のユーザに戻ります。
このままでは、phpからPostgreSQLにアクセスできない(認証が通らない)ので、pg_hba.confファイルを変更します。$ sudo vi /etc/postgresql/12/main/pg_hba.conf'local'の値が'peer'になっているところを'md5'に変更します。
pg_hba.conf(Before)# TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections only local all all peerpg_hba.conf(After)# TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections only local all all md5ここでリブートしてください。
次にテーブルの作成をします。
まずご自分のitunesから'ライブラリ.xml'ファイルを作成します。
itunesの、
ファイル->ライブラリ->ライブラリを書き出す
を選択すると、xmlファイルを書き出すダイアログになるので、適当な名称で書き出してください。Raspberry piに持っていくので、半角英数にしておくとよいです。SSHなどでRaspberry piに転送します。ここでは、lib.xmlとします。これをplayer/plist_tools/ に配置します。そしてツールでCSVファイルに変換します。$ python3 plist2csv.py lib.xml track_id integer, name integer, artist integer, album integer, genre integer, size integer, total_time integer, track_number integer, track_count integer, year integer, bit_rate integer, normalization integer, persistent_id integer, compilation integer, ['Track ID', 'Name', 'Artist', 'Album', 'Genre', 'Size', 'Total Time', 'Track Number', 'Track Count', 'Year', 'Bit Rate', 'Normalization', 'Persistent ID', 'Compilation'] $出てくるメッセージは無視してください。これでtrack_data.csv ができます。
次にデータのテーブルを取り込みます。先ほど作ったtrackdbに接続し、テーブルを作り、csvファイルをインポートします。trackdbの接続をする際にはパスワードを聞かれます。入ったら、CREATE TABLEコマンドを実行しますが、コマンドのテキストがplayer/plist_tools/head.txtにあるので、コピーペーストしてください。あとは\copyコマンドで作ったtrack_tableにcsvを読み込みます。head.txtCREATE TABLE track_table ( track_id integer, name varchar, artist varchar, album varchar, genre varchar, size integer, total_time integer, track_number integer, track_count integer, year integer, bit_rate integer, normalization integer, persistent_id char(16), compilation boolean, PRIMARY KEY (track_id));$ psql -d trackdb Password for user pi: psql (12.2 (Ubuntu 12.2-4)) Type "help" for help. trackdb=> CREATE TABLE track_table ( trackdb(> track_id integer, trackdb(> name varchar, trackdb(> artist varchar, trackdb(> album varchar, trackdb(> genre varchar, trackdb(> size integer, trackdb(> total_time integer, trackdb(> track_number integer, trackdb(> track_count integer, trackdb(> year integer, trackdb(> bit_rate integer, trackdb(> normalization integer, trackdb(> persistent_id char(16), trackdb(> compilation boolean, trackdb(> PRIMARY KEY (track_id)); CREATE TABLE trackdb=> \dt List of relations Schema | Name | Type | Owner --------+-------------+-------+------- public | track_table | table | pi (1 row) trackdb=> \copy track_table FROM 'track_data.csv' with csv COPY 7868 trackdb=> \q $データベーステーブルができているかテストをします。
$ sudo su $ cd /var/www/html/player/ $ php test.phpアーティストのリストが出ればOKです。エラーになる場合はメッセージに従ってください。認証エラーなどが起きます。
お疲れさまでした。以上で、設定は終わりです。
ブラウザから以下のURLをたたいてください。http://<Raspberry Piのアドレス>/player/
[artist] のボタンが出るので、ボタンを押すとartsit->album->trackの順に指定していき、trackを指定することで音楽がスタートするはずです。
あとがき
曲ごとに開始音がするのがイマイチなのですが、BGM用途には使えると思います。
自分用途にささっと作ったので、非常に味気ないものになっています。HTML部分は超絶シンプルにしているので、いろいろ改造してください。
データ管理をPostgreSQLに任せたため、phpでの処理内容をかなりシンプルにできたと思います。プレイリスト再生とかランダム再生とかにも挑戦していきたいと思います。
- 投稿日:2020-08-09T00:48:26+09:00
初心者がSlimをインストールしてみた!
Laravelで幾つかアプリケーションを作ったので、もう一つくらいフレームワークを触ってみようとSlimを導入してみました。
Slimとは
Slimとは軽量フレームワーク(マイクロフレームワーク)の一種であり、アプリケーションが動くのに必要な機能を最低限のみ兼ね備えたフレームワークです。
ちなみに、LaravelやCakePHP等たくさんの機能を兼ね備えたフレームワークはフルスタックフレームワークといい、マイクロフレームワークとは対照的なものになります。
その為、フルスタックフレームワークと比べ処理速度が速く、ファイル容量も軽くなります。インストール手順
スペック
OS:Windows10 Home 64bit
php Version: 7
Composer version:1.8.6Composerのインストール
Slimをインストールする際は、Laravelと同様にComposerを予めインストールされてあることが必要になります。
今回は既にインストールした状態で行った為、こちらの記事をご覧になっている方でComposerをまだインストールしていない方は、以下のURLからインストールして下さい。
Composer:https://getcomposer.org/コマンド入力
Composerをインストールしたら、コマンドプロンプトでSlimを入れたいディレクトリまで移動し、以下のコマンドを入力します。
composer create-project slim/slim-skeleton:^3.* プロジェクト名インストールは概ね3~5分程度で完了します。
以下のようになったら、インストール成功です!
ディレクトリに、プロジェクト名(今回は「slim_test」)のフォルダが作成され、以下のファイルが作成されました。
Hello Worldしてみた
ローカルサーバーで「\templates\index.phtml」のサンプルページを表示させます。
インストールしたプロジェクトにディレクトリを移動させ、以下のコマンドを入力します。php -S localhost:8888 -t public public/index.phpコマンド実行後、ブラウザで「localhost:8888/World」と打ち込むと、下図のサンプルページが表示されます。
まとめ
今回はマイクロフレームワークであるSlimをインストールしました。
とりあえず何かしらのアプリケーションを作成し、Laravelとの比較を行ってみようと思います。参考文献