CakePHP3で詰まった部分

開発環境は、Windows 7 Professional(32bit)+XAMPP v3.22+CakePHP 3.5.8。


【問題】テンプレート部分でtitleを設定したい
テンプレートの上部にて、

<?php
	$this->assign('title',__('このページのタイトルです'));
?>


【問題】metaタグのkeywordとdescriptionを設定したい

<?= $this->Html->meta('keywords', '') ?>
<?= $this->Html->meta('description', '') ?>

参考リンク
Html - 3.7
メタキーワード(meta name=”keywords”)はSEOに重要なのか? - ウェブ企画ラボ


【問題】HTTP GETメソッドのパラメータの値を取得し、テンプレート側で表示したい

<?php
$this->request->getQuery("keyword");
?>

参考リンク
リクエストとレスポンスオブジェクト - 3.7


【問題】$this->paginateメソッドにて、1ページ当たりの結果を指定したい
コントローラ内にて、

<?php
$this->paginate = ['limit' => 15];
?>

を先に追加する。

参考リンク
CakePHP 3 Pagination - Stack Overflow


【問題】usersテーブルにユーザーデータを新規登録できるが、Authコンポーネントでログインできない(identifyメソッドが常にfalseを返す)

【解決】usersテーブルのemail,password,usernameはvarchar型で定義していて、charsetはutf8mb4。サイズは35や80だったが、3つとも、varchar(255)に設定して、usersテーブルのデータをすべて消してから、改めて新規登録すると、問題なくログインできた。

utf8mb4だと1文字4バイト。ハッシュ化されたpasswordは最大60文字入らないといけない。だから、60×4で、最低でもvarchar(240)は必要ということだろうか?マルチバイト文字じゃなくてもutf8mb4だと1文字4バイトとなるのだろうか?そこは不明。

そこで、検証してみるために、passwordのvarcharを60に設定し、新しく、ユーザーデータを新規登録し、ログインしてみると問題なくできた。つまり、utf8mb4でも、マルチバイト文字じゃないと、1文字1バイトとして計算される。

さらに調べてみると、DefaultPasswordHasher.phpのhashメソッドを用いると、PHPのpassword_hash関数を内部で使っていて、そのハッシュタイプは、PASSWORD_DEFAULTという定数になっていて、それは現時点では、PASSWORD_BCRYPTで、常に60文字。そして、格納先は60文字以上、格納できるようにすべしと書いてある。

参考リンク
ログイン[Auth-> identify()] CakePHP 3で常にfalse - CODE Q&A 問題解決
『cakephp3 auth identify』で検索して、このサイトを発見し、解決した。
PHP: 定義済み定数 - Manual
PHP: password_hash - Manual


【問題】なぜか1回だけしか投稿できない。調べてみると、エラーメッセージには、なぜか、postsテーブルのusernameカラムがユニークキー扱いになっている。(そのusernameはすでに存在します、となる)
【解決】原因は何かというと、bakeで自動生成したModelのPostsTable.php内にあるbuildRulesメソッドで定義されている、$rules->isUniqueにusernameが設定されていたことだった。CakePHPの規約なのか分からないが、bakeで自動生成した際にusernameというカラムをテーブルに設定していると、自動的に、isUnique扱いにしてしまうのだろうか?


【問題】コンポーネントがある場所は?
【解決】vendor/cakephp/cakephp/srcディレクトリ内。


【問題】 containしているテーブルのデータをソートしたい
【解決】モデル側で書く場合とコントローラ側で書く場合の2つがある。

参考リンク
[CakePHP3] アソシエーション先のデータをソート | ガジェットとネイルが好き。
CakePHP3のcontainを操る - Qiita

【問題】inputタグのbuttonで、class属性をfollow_btnにするとボタンがなぜか、表示されず消える。
【解決】class属性名をfollow_bnにすると、きちんと表示された。原因はよく分からない。class属性自体を消したら、きちんと表示される。


【問題】あるコントローラーで、そのコントローラとは関係のないテーブルから情報を引っ張りたい。
【解決】そのコントローラーの上部に

<?php
use Cake\ORM\TableRegistry;
?>

を追加し、そのコントローラにinitialize()を書いて、そこで呼び出しておくか、使いたいアクションの中で、呼び出すのか好きにできる。

<?php
     public function initialize(){
         parent::initialize();
         # $this->変数名 = TableRegistry::get('テーブル名。頭文字は大文字');
         $this->testtbl = TableRegistry::get('Users');
     }
?>

使用する際は、

<?php
		$result = $this->testtbl->find();
?>

などのように行う。

※3.6.0では、方法が変わったみたいなので、追記しておく。

<?php
  # $this->変数名 =TableRegistry::getTableLocator()->get("テーブル名。頭文字は大文字");
  $this->testtbl = TableRegistry::getTableLocator()->get('Users');
?>

【参考リンク】
テーブルオブジェクト - 3.7
【CakePHP3】無関係な複数テーブルのデータを一つのコントローラで扱う - めめんと
cakephp で一つのモデルから複数のDBテーブルを参照する - Qiita


【問題】CSSを使う際に、外部CSSファイル内にURLが指定されていたら、どのようにすればいいのか?
【解決】
例えば、cssディレクトリ内にあるcssファイル内で、1つ上の階層にあるwebfontsディレクトリのフォントファイルを読み込む場合、同じ、webrootディレクトリに、webfontsディレクトリをいれればOK。


【問題】今、ログインしてアクセスしているユーザーのusersテーブル上のユーザーのID番号やユーザー名を知りたい。
【解決】
この方法は、Authコンポーネントを用いて、ログイン/ログアウトをしているならば使用できる。
例えば、usersテーブルに、idやusernameなどのカラムがあるならば、
以下のようにして取得できる。

<?php
$id = $this->request->session()->read('Auth.User.id');
$username = $this->request->session()->read('Auth.User.username');
?>

【追記】session()は、CakePHP 3.6からは、近々、廃止予定というエラーメッセージ(Deprecated)が表示されるので注意。getsession()というものになるみたいです。
参考リンク
Session - 3.7


【問題】ビューのフォームから送信した値をコントローラで受け取る方法は?
【解決】

<?php
$value = $this->request->getData('カラム名');
?>

で受け取ることができる。カラム名がなければ、nullが返され、あるけれど、値が入力されていなければ、空文字が返される。

原因が分かれば、本当に詰まらないことに自分の人生の時間を取られてしまっている。それをできる限り、最少にしたい。


【問題】フォームから送られた値を修正したり、追加する
【解決】

<?php

# 新たにデータを入れたい場合は、patchEntityの前に入れる
$this->request->data("column_name","value");
$user = $this->Users->patchEntity($user, $this->request->getData());

?>


【問題】リファラにリダイレクトするには?
【解決】

<?php
return $this->redirect($this->referer());
?>


【問題】createdやmodifiedが自動更新されるようにする
テーブル内にcreatedやmodifiedという名前のカラムがあるという前提で、
【解決】
/src/Model/Table内のファイルのinitializeメソッドの中に、

<?php
$this->addBehavior('Timestamp');
?>

を追加するだけでOK。もし、すべてのテーブルに共通化しておきたければ、今作ってあるTableと継承元のTableクラスの間に、AppTableを作って、そのinitializeメソッド内に上記のaddBehaviorを入れておくといい。
例えば、ItemsTableであれば、

ItemsTable extends Table

AppTable extends Table
ItemsTable extends AppTable

※動作を確認して見ると、保存する際に、saveメソッドを使った場合のみ追加されて、insert、updateメソッドだと、追加だったり、変更されない。


【参考リンク】
クエリービルダー - 3.7
CakePHP3を触ってみました 〜あれ、updatedは?〜 | 日記の間 | あかつきのお宿