PHP
Library
Framework
- Symfony
- CakePHP
- FuelPHP: 2010年誕生。
- Codeigniter: シンプル、軽量。
- Zend
- Laravel: 2011年誕生。
- Phalcon
- Yii - Wikipedia
Template
いろいろある。
- Blade: Laravel標準。
- DIV: 1ファイルでシンプル。大規模には向かない。
- Smarty: 万能。
- Twig: 拡張はしにくい。
使うとしたら、歴史の長いSmarty。
- Smartyとは?基礎知識と具体的なメリットをわかりやすく解説 - システム開発のプロが発注成功を手助けする【発注ラウンジ】
- Smartyとは? - smarty @Wiki - atwiki(アットウィキ)
高速らしい。
そもそもテンプレートエンジンがいるのかどうかという議論がある。
- Smarty って、要らなくない? — INWORKS
- 素のPHPはもはやテンプレートエンジンとしては使えない - ぱせらんメモ
- 本当に倒すべきだったのは jQuery ではなくテンプレートエンジンだった - fsubal
- サーバサイドHTMLテンプレートからの脱却のススメ (1/4)|CodeZine(コードジン)
UI/UXを突き詰めると、JavaScriptを使わざるを得ず、サーバーテンプレートエンジンは初回だけなので、いっそのことJSで全部やろうというのが最近の流れの模様。
PHP自体が一種のテンプレートエンジンという主張がある。が、関数をあれこれ書く必要があり、可読性が悪い。
SmartyよりTwigのほうが性能が上とか。
「Volt: テンプレートエンジン — Phalcon 3.0.2 ドキュメント (Japanese / 日本語)」。高速フレームワークのPhalconではVoltを使っている。
Twig
- 素のPHPはもはやテンプレートエンジンとしては使えない - ぱせらんメモ
- Templating Engines in PHP |Articles - Fabien Potencier
- PHPテンプレートエンジンを使おう Twig編 | AkisiのWEB制作日記
- Decoded Node: Smarty vs. Twig (a benchmark done poorly)
- GitHub - AliShareei/smarty-vs-twig-benchmark: A benchmark of up-to-date versions of Smarty and Twig templating engines
- GitHub - dominics/smarty-twig-benchmark: A benchmark of up-to-date versions of Smarty and Twig templating engines
- The fastest template engine for PHP | by Serge Gotsuliak | Medium
- Pure PHP/HTML views VS template engines views - Stack Overflow
Twig v3のほうが速いらしいが、Smarty v3のほうが速いというデータもある。
Smarty
- GitHub - smarty-php/smarty: Smarty is a template engine for PHP, facilitating the separation of presentation (HTML/CSS) from application logic.
- Smarty Documentation
「Pure PHP/HTML views VS template engines views - Stack Overflow」が決定的だった。Smartyの開発者がSmartyのほうがTwigより速いと回答していた。2012年。Smartyでいいと思う。
ORM
Ref:
いろいろある。Doctrineが有名。
- Doctrine: Symfonyで採用。有名。
- Eloquent
- Propel: Symfony v1.2で採用されていた。
- PHP activerecord
- PHPDAO
- PDO: PHP標準。
- Xyster
ただ、速度を優先する場合、PDOが最速になるらしい。
ORMは別になくてもいいか。
Migrate
- Phinx
- Doctrine Migrations
「PHPで「Doctrine Migrations」を使ってみる」
CakePHPに採用されているPhinxのほうが人気なのでPhinxを使ったほうがよいだろう。
Test
- PHPUnit
Search
検索キーワードをフォームから受信後、DBにSQLで検索をかけて取得結果を返すのが基本。
それとは別に、検索用のアプリにリクエストを受け渡しして検索するという方法がある。どちらでもいけるような、ドライバーのライブラリーがある。
- Laravel Scout - Laravel 9.x - The PHP Framework For Web Artisans
- teamtnt/tntsearch: A fully featured full text search engine written in PHP
- ruflin/elastica - Packagist (40 Best PHP Libraries For Web Applications in 2022)
検索サービスで有名なのは以下。
- ElasticSearch/OpenSearch
- MeiliSearch
- Algolia
- Sphinx Search (Sphinx | Open Source Search Engine)
- Apache Solr (Welcome to Apache Solr - Apache Solr)/Apache Lucene (Apache Lucene - Welcome to Apache Lucene)
Laravel Scoutでtntsearchを使う方法がある (Laravel Scout + TNTSearchによる小規模プロジェクトへの全文検索機能の追加 #PHP - Qiita/Laravel ScoutとTNTSearchを使用してサイト全文検索を実装してみる – helog)。
「Packagist」の検索結果をみても、tntsearchが特に人気の模様。
Laravel
人気の理由
「Laravelの人気を大検証 何が凄いの? | テクフリ」
CakePHPのほうが早い。
- 簡単にマスター可能
- 自由度が高い
Getting Started
Configuration
Configuration - Laravel 5.8 - The PHP Framework For Web Artisans
Environment Configuration
DotEnvライブラリーを使っており、ルートディレクトリーの.env.exampleを.envにリネームするとこの設定を利用する。
Directory Structure
Directory Structure - Laravel 5.8 - The PHP Framework For Web Artisans
- app
- bootstrap
- config
- database
- public
- resources
- routes
- storage: フレームワークで自動生成される、コンパイル済みのBladeテンプレート、ファイル系セッション、ファイルキャッシュ類を格納。app, framework, logsがある。
- app: アプリ生成ファイルの格納。
- framework: フレームワーク生成ファイルとキャッシュ。
- logs: ログ。
- tests
- vendor
The Basics
Routing
Basic Routing
routes/web.phpでURL別のアクセス時 (ルーティング) の処理 を設定する。
web.phpはwebミドルウェアグループに割り当てられていて、CSRFガードなどが入っている。
Route::get('/user', 'UserController@index');
上記のような書式で、パスとアクションを対応付ける。
Named Routes
ルートに名前を付けて、後で流用・参照できる。
Database
Migrations
Database: Migrations - Laravel 5.8 - The PHP Framework For Web Artisans
マイグレーションはデータベースのバージョン管理のようなもの。データベーススキーマを簡単に修正・共有を可能にする。
Running Migrations
以下のコマンドで用意済みのマイグレーションを実行する。
php artisan migrate
Error
Using Docker I get the error: "SQLSTATE[HY000] [2002] No such file or directory"
php - Using Docker I get the error: "SQLSTATE[HY000 [2002] No such file or directory" - Stack Overflow]
PHPのPDOをDockerコンテナ内で使おうとしたところ、"No such file or directory" エラーが発生した話 #docker-compose - Qiita
dockerで.envのDB_HOSTの指定間違い。dockerのservice名をホスト名に指定する必要がある。
local.ERROR: could not find driver
[2024-07-02 15:35:42] local.ERROR: could not find driver (SQL: select * from `sessions` where `id` = 0cDH7URcrsFzC7hPYlbAQcezNVjdDM16OJh1aCSZ limit 1) {"exception":"[object] (Illuminate\\Database\\QueryException(code: 0): could not find driver (SQL: select * from `sessions` where `id` = 0cDH7URcrsFzC7hPYlbAQcezNVjdDM16OJh1aCSZ limit 1) at /var/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664, Doctrine\\DBAL\\Driver\\PDO\\Exception(code: 0): could not find driver at /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDO/Exception.php:18, PDOException(code: 0): could not find driver at /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:40)
このエラーはphpのモジュール不足。LaravelはPDOを使う。mysqliではなく。
RUN docker-php-ext-install pdo_mysql
これが必要。
Other
Facade
【Laravel】ファサードとは?何が便利か?どういう仕組みか? #初心者 - Qiita
インスタンスメソッドをstaticメソッドのように使うための仕組み。ややこしいだけなのでいらないかなと思う。
CakePHP
Basic
Structure
- bin: cakeコマンド類。
- config: CakePHPの全体設定にかかるファイル群。
- logs
- plugins
- src
- tests
- tmp
- vendor
- webroot: アプリケーションルート。
Convention
Ref: CakePHP の規約 - 3.10.
テーブル名は小文字、複数形、アンダーバー区切り。
Routing
Ref: ルーティング - 3.10.
config/routes.phpでルーティングの基本が設定される。ここのコードで、後述のMVCがある程度自動設定されている。
ほかに、アプリケーショントップの/アクセス時の処理を行う、重要な設定もここで行う。/はMVCの命名規則になっていないので、ここは手動設定?的なことが必要になる。基本はここがLoginになる。
Router::scopeとRouter::connectを使って処理する。
scopeはルーティングのグループ化。connectで個別に紐づける。
Router::scope('/blog', ['plugin' => 'Blog'], function ($routes) {
$routes->connect('/', ['controller' => 'Articles']);
});
例えば、上記は/blogへのアクセスをArticlesController.index()に紐づける。
connectの第二引数にいろいろパラメーターを指定できて、actionのメソッドを指定できたりする。
Controller
AppControllerを継承して定義する。indexメソッドを最低限実装しておく。
XXXControllerのXXXの小文字部分がURLになっていて、これでアクセスするとindexが発動する。
メソッド名が、パスになっていて、それで表示される。このパスメソッドを一般的にacitonと呼んでいる。
$this->request
リクエストに関する情報が入っている。PHPの$_FILESとか$_POSTなどを使わなくも、これを使えばよくなる。
2次元配列にもなっているが、1次元配列部分はプロパティーとしてもアクセスできる。
- params: 送信されたすべてが入っている。
- data: POSTのボディー。
- query: URLクエリー。
- url
- base
- webroot
- here:
特にdataをよく使うだろう。
Component
Ref: コンポーネント - 3.10.
コントローラー間で共有されるロジックのパッケージ。認証など処理を共通化できる。
$this->loadComponent('コンポーネント');
これでコンポーネントを読み込めて、以降は$this->コンポーネントでインスタンスにアクセスできる。
View
Ref: ビュー - 3.10.
Controllerで表示もできるものの、Viewで表示部分をメインにすることもできる。
Viewはビューテンプレートとレイアウトで大きく構成される。
テンプレートは実際のページ表示用。レイアウトは、テンプレートを含み、ヘッダー、フッターなどをまとめている。
レイアウトを用意して、その中にビューテンプレートを埋め込んで表示するイメージ。これにより、ページ全体でレイアウトを統一しやすくなる。
テンプレートファイルのデフォルトの拡張子は.ctp (CakePHP Templateの略)。
Template
src/TemplateがViewテンプレート。ここにコントローラー名に対応したディレクトリーを作って、そこにViewテンプレートを作成する。
例えば、src/Template/Helloにindex.ctpを用意する。
Controller側で、public $autoRender = true;があると、テンプレートを使う。命名規則で自動的に。
そして、$this->viewBuilder()->autoLayout(false) でレイアウトの使用可否を設定する。
index.ctpはテンプレートなので、ここにPHPコードを記入してもOK。
テンプレート内では変数が使える。この変数は、コントローラーで$this->setで設定されたものになる。
基本的には以下のようなphpコードで埋め込む。
<?php echo $variable; ?> <?= $variable ?>
Layout
デフォルトで使用するレイアウトは、src/Template/Layout/default.ctpにある。
ここにファイルを追加することが、レイアウトの作成になる。
レイアウト内で、テンプレートを表示する関数類がある。
- $this->fetch('content'): ビューテンプレート
Controller側では、$this->viewBuilder()->layout('Hello');でレイアウトを指定できる。
Element
レイアウトよりも小さいパーツ。ボタンなど、流用するようなものをElementとして用意できる。
$this->element(エレメント名, [キー=>値,...])で表示できる。
src/Template/Element内に作成する。エレメント名.ctpで作成する。キー=>値の関係で、パラメーターを渡せる。
Controllerのaction内でsetで、layout内で使用する変数を指定できる。これで、layout-element間で変数を渡せる。
Model
DBを処理する部分。
具体的には、テーブルクラス (テーブルへのアクセス処理) とエンティティークラス (テーブルから取り出した1行分のデータのための列の表現) で実現する。
CakePHP内では、モデル名Table/モデル名でそれぞれ命名する。テーブルクラスは複数形、エンティティークラスは単数形で扱う。テーブル自体も複数形。
CakePHPでDBにアクセスする際には、まず設定ファイルにDBの情報を用意する。Config/app.phpのDatasourcesのdefaultに入力する。
モデル自体は、src/Model内に配置する。EntityとTableディレクトリーでそれぞれ用意する。
継承するだけで、基本的な動作は機能する。
Controllerから、これらのモデルにアクセスする。
DBへのアクセスにはORMのQueryBuilderとSQLベースのConnectionManagerがある。
ConnectionManager
Ref: データベースの基本 - 3.10.
SQLをそのまま発行するタイプのクラス。
$conn = ConnectionManager::get('設定名'); $statement = $conn->execute('SQL'); $statement->fetchAll('num' | 'assoc');
ConnectionManagerからConnectionインスタンスを取得し、DBオブジェクトとして扱う。
executeでステートメントを取得し、statementのメソッド呼び出しのタイミングでSQLが実行される。
実行結果は配列。numを指定すると、キーが数字、assocだと項目名 (カラム名) がキーになる。基本はassocを指定するとよい模様。
TableRegistry
Ref: Saving Data - 3.10.
use Cake\ORM\TableRegistry; // Prior to 3.6 use TableRegistry::get('Articles') $articlesTable = TableRegistry::getTableLocator()->get('Articles'); $article = $articlesTable->get(12); // Return article with id 12 $article->title = 'CakePHP is THE best PHP framework!'; $articlesTable->save($article);
組み込まれていないテーブルはgetで取得する必要がある。
Basic
Query logging
Ref: Database Basics - 3.10.
// クエリーログを有効 $conn->logQueries(true); // クエリーログを停止 $conn->logQueries(false);
CakePHPのクエリービルダーの実際のSQLを確認できる。
「[cakephp Queryオブジェクトから実行したSQL生成 #MySQL - Qiita]」にあるように、他に、sql()やgetValueBinder()->bindings();でも取得できる。
$this->log($this->MBase->findMBase($options)->sql());
データの取り出しと結果セット
Ref: データの取り出しと結果セット - 3.10.
hydrate(false)でfindなどした際の結果を、オブジェクトではなく配列にできる。
Migration
Ref:
PHPファイルでDBのスキーマ変更を行うための仕組み。VCSでDB設定を管理でき、コマンドでDBの設定などを行える利点がある。
CakePHPでは、Phinxをマイグレーションに使っており、コマンド類はPhinxのラッパーになっている。細かいことはPhinxにあたる必要がある。
config/Migrationsディレクトリーに、マイグレーションファイルを配置し、以下のmigrationsコマンドの実行でDBにテーブルを作成できる。
bin/cake migrations migrate bin/cake migrations rollback
戻す場合はrollback。
マイグレーションファイルは、config/Migrationディレクトリーで、YYYYmmddHHMMSS_MigrationName.phpというように、作成日を入れて用意する。
自分で手作業でマイグレーションファイルを作成できるが、bakeコマンドでひな形を用意できる。これを使ったほうがおそらくいい。
マイグレーションファイル
Valid Column Types
Phinx
で一般的に利用可能なフィールドの型は次の通り:
- string
- text
- integer
- biginteger
- float
- decimal
- datetime
- timestamp
- time
- date
- binary
- boolean
- uuid
このほかに、以下も可能。
In addition, the MySQL adapter supports enum
, set
, blob
, tinyblob
, mediumblob
, longblob
, bit
and json
column types (json
in MySQL 5.7 and above). When providing a limit value and using binary
, varbinary
or blob
and its subtypes, the retained column type will be based on required length (see Limit Option and MySQL for details);
In addition, the Postgres adapter supports interval
, json
, jsonb
, uuid
, cidr
, inet
and macaddr
column types (PostgreSQL 9.3 and above).
既存のテーブルにカラムを追加
以下のコマンドでカラム追加を含むコードを作成できる。
bin/cake bake migration AddPriceToProducts price:decimal
<?php use Migrations\AbstractMigration; class AddPriceToProducts extends AbstractMigration { public function change() { $table = $this->table('products'); $table->addColumn('price', 'decimal') ->update(); } }
addColumnの3引数にいろいろパラメーターを指定できる。
public function change() { $table = $this->table('m_grade'); $table->addColumn('image_data', 'blob', [ 'default' => null, 'limit' => Phinx\Db\Adapter\MysqlAdapter::BLOB_MEDIUM, 'null' => true, 'after' => 'image_path', ]); $table->update(); }
afterで直前の列を指定できる。
Blobの対応
- CakePHP Migrations の limit オプションについて #PHP - Qiita
- cakephp3 の Migration で mediumblob を認識させる #MySQL - Qiita
CakePHP自体は、MySQLのblobには直接は対応していない。binaryでひな形を作って、手動でlimitを変更する。
列の更新
Updating columns name and using Table objects
If you use a CakePHP ORM Table object to manipulate values from your database along with renaming or removing a column, make sure you create a new instance of your Table object after the
update()
call. The Table object registry is cleared after anupdate()
call in order to refresh the schema that is reflected and stored in the Table object upon instantiation.https://book.cakephp.org/migrations/3/en/#updating-columns-name-and-using-table-objects
updateした後に、CakePHPのORMを使う模様。
// Update exisiting column default row data from path. $mbaseTable = TableRegistry::get('m_base'); foreach($mbaseTable->find() as $row) { $file_path = WWW_ROOT . $row->LOGO_PATH; if (is_readable($file_path)) { $raw_data = file_get_contents($file_path); if ($raw_data) { $row->LOGO_DATA = $raw_data; $mbaseTable->save($row); } } }
こういうイメージ。
Debug
Ref:
CakePHP関係のクラスであればlogメソッドがある。
他に、Cake\Log\Log::write()
のstaticメソッドもある。
Log::debug('text')などで使う。
- pr: print_r+pre
- debug:
- dd: debug+die
Log::debug()を使うと、logs/debug.logに出力される。
Topic
Pagination
コントローラーのpublic $paginateプロパティーに、ページネーションの設定を記載する。
その後、$this->paginate(テーブル)で自動的にページネーションされたデータを取得できる。
Test
Ref: テスト - 3.10.
CakePHPはPHPUnitでのテストに対応している。
まずDBをテスト用に置換する。config/app.phpのDatasourcesにtestを追加しておく。
bakeコマンドの中で、fixtureとtestが関係するコマンド。
まずfixtureでテストデータを作成する。
cake bake fixture <model>
modelでMVCの一単位を指定する。
続いて以下のコマンド群でMVCのテストファイルを作成。
cake bake test controller <Controller> cake bake test entiity <Entity> cake bake test table <Table>
testsディレクトリー内の以下に生成される。
- Fixture: テーブル名Fixture.php
- TestCase: クラス名Test.php
Test.php内で、以下のコードで該当Fixtureを自動読み込みする。
public $fixtures ['app.テーブル名'];
TestCaseクラスがCakePHP特有で、これでうまくやっている模様。
namespace App\Test\TestCase\Model\Table;
use App\Model\Table\ArticlesTable;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;
class ArticlesTableTest extends TestCase
{
public $fixtures = ['app.Articles'];
public function setUp()
{
parent::setUp();
$this->Articles = TableRegistry::getTableLocator()->get('Articles');
}
public function testFindPublished()
{
$query = $this->Articles->find('published');
$this->assertInstanceOf('Cake\ORM\Query', $query);
$result = $query->enableHydration(false)->toArray();
$expected = [
['id' => 1, 'title' => 'First Article'],
['id' => 2, 'title' => 'Second Article'],
['id' => 3, 'title' => 'Third Article']
];
$this->assertEquals($expected, $result);
}
}
そして、setUp内で、まずはテーブルをメンバー変数に格納して、それを参照する形。
Error
PHP message: PHP Warning: file_put_contents(/var/www/html/logs/error.log) [<a href='http://php.net/function.file-put-contents'>function.file-put-contents</a>]: failed to open stream: Permission denied in /var/www/html/vendor/cakephp/cakephp/src/Log/Engine/FileLog.php on line 133
起動してトップ画面を開くと、logs/error.logに以下のエラー。
2024/04/04 07:27:48 [error] 30#30: *1 FastCGI sent in stderr: "PHP message: PHP Warning: file_put_contents(/var/www/html/logs/error.log) [<a href='http://php.net/function.file-put-contents'>function.file-put-contents</a>]: failed to open stream: Permission denied in /var/www/html/vendor/cakephp/cakephp/src/Log/Engine/FileLog.php on line 133 PHP message: PHP Warning: file_put_contents(/var/www/html/logs/error.log) [<a href='http://php.net/function.file-put-contents'>function.file-put-contents</a>]: failed to open stream: Permission denied in /var/www/html/vendor/cakephp/cakephp/src/Log/Engine/FileLog.php on line 133
パーミッションの設定がDockerに不足している?
「CakePHP Error Warning: file_put_contents(/var/www/html/logs/error.log) #PHP - Qiita」にあるように、
docker exec -ti php_jaccs_auto bashでchmod a+w ./logs/*相当を実行すると解決した。ただし、事前にWindowsのExplorerで書き込みを許可しておく必要がある。git bashのchmodはWindowsには機能しない。
Warning: Warning (512): SplFileInfo::openFile(/var/www/html/tmp/cache/persistent/myapp_cake_core_translations_cake_en__u_s) [<a href='http://php.net/splfileinfo.openfile'>splfileinfo.openfile</a>]: failed to open stream: Permission denied in [/var/www/html/vendor/cakephp/cakephp/src/Cache/Engine/FileEngine.php, line 398]
Ref: CakePHP3でWarning Error: SplFileInfo::openFile()エラーが発生した場合の対処方法 | エス技研.
app.phpにmask=>0666を追加する。既存のキャッシュファイルは削除しておく。
dockerのアクセス権www-dataの問題の気がする。