【ふくだ学習録】CakePHP3入門 part2【11日目】

# ふくだ学習録とは？
ふくだが学習したことの備忘録。
目に見える形で残すことによってやる気を出す個人的な作戦です。
他人に見せるように書いているわけではないので、すごく読みにくいです。

### 読了した本
[データベースエンジニア養成読本 [DBを自由自在に活用するための知識とノウハウ満載!]](https://www.amazon.co.jp/dp/4774158062/ref=cm_sw_r_tw_dp_U_x_FPphEbQFPPQRM )
[ゼロから作るDeepLearning](https://www.amazon.co.jp/dp/4873117585/ref=cm_sw_r_tw_dp_U_x_y3UiEbF3P6EG7)

# CakePHP3入門を読んで

### ビューの自動レンダリング指定方法
コントローラ内で設定する。

```php:〇〇Controller.php
<?php
namespace App\〇〇Controller;

class 〇〇Controller extends AppController {
  public $name = 'Hello';      #メンバ変数として、コントローラの名前を設定する。
  public $autoRender = false;  #ビューの自動レンダリング設定
  public function index(){
    echo "hello world";
  }
}
```

### CakePHP内のプロパティ前のアンダースコアについて
アンダースコア1つは、protected修飾子を意味し、
アンダースコア2つは、private修飾子を意味しており、それぞれを判別するためにつけている。
必ずしもつけなくては動作しないものではなく、古い慣習的なもの。

public修飾子：`public $public`;
protected修飾子：`protected $_protected`;
private修飾子：`private $__private`;

※過去に各修飾子が存在しなかった頃に、それぞれを見分けるために作られた模様。

### フォワード、リダイレクト
特定のアクションが行われた際に、別のアクションを行いたいときに使用するもの。
（例えばログインしていないユーザーがアクセスしてきたときは、ログインページに飛ばしたい　とか）

**フォワード**
サーバー側のプログラムで別ページに移動させるもの。
実行しているアクションが変わるだけでURLは変わらないし、別サイトへの遷移などはできない。

```php:
$this->setAction(アクション名)
```

**リダイレクト**
ブラウザに対して別ページにジャンプさせるように要求するもの。
実際にURL遷移するし、別サイトへの遷移などもさせられる。

```php:
$this->redirect(アクション名)
```

### レイアウト機能のONOFF
Controllerに用意されている`ViewBuilder`というオブジェクトの中の`autoLayout`というメソッドを呼び出す。

```php:
$this->viewBuilder()->autoLayout(false); #この場合ではレイアウト機能は働かない。
```

### コントローラごと（アクションごと）に個別のレイアウトを読み込む方法
`src/Template/Layout`内にファイルを作成して、読み込みたいController内で記述する。

例：Helloコントローラでのアクション実行時のみ、default.ctpではないレイアウトにしたい場合。

```php:src/Template/Layout/hello.ctp
<!DOCTTYPE>
<html lang="ja">
<head>
# 諸々記載
</head>
```

```php:src/Controller/HelloController.ctp
<?php
namespace App\Controller;

class HelloController extends AppController {

  public function initialize() {
    $this->name = 'Hello';
    $this->viewBuilder()->autoLayout(true);
    $this->viewBuilder()->layout('Hello'); #ここで設定する
  }

  public function index() {
  }

}
```

### 文字コードの設定
レイアウトファイル内で、`<?= $this->Html->charset(); ?>`と記載。
返り値として、`<meta http-equiv="Content-Type" content="text/html; charset="utf-8"/>`と返ってくる。

`$this->HTML`にはHtmlHelperというクラスのインスタンスが収められており、ビューの出力を支援する機能を提供してくれる。

```php:src/Template/Layout/hello.ctp
<!DOCTTYPE>
<html lang="ja">
<head>
  <?= $this->Html->charset(); ?>
  # 諸々記載
</head>
```

### タイトルの設定
レイアウトファイル内で、

```php:
<?= $this->fetch('title'); ?>
```
と記載。
すると、コントローラ内に記載していた`$name`に設定されている値がtitleタグ内に入った状態で吐き出される。

### css,javascriptファイルの読み込み
**cssファイルの読み込み**
hogehoge.cssを読み込みたい場合、

```php:
<?= $this->Html->css('hogehoge') ?>
```

と記載する。これでwebrootのcssディレクトリ内の`hogehoge.css`を読み込んでくれる。

**jsファイルの読み込み**
hogehoge.jsを読み込みたい場合、

```php:
<?= $this->Html->script('hogehoge') ?>
```

と記載する。これでwebrootのjsディレクトリ内の`hogehoge.js`を読み込んでくれる。


### コンテンツの出力

```php:
<?= $this->fetch('content') ?>
```

と記載する。
この部分にビューテンプレートをレンダリングした結果が出力されてくる。

### エレメントの利用
headerやfooterなどのレイアウトの内部で使う共通化したパーツの呼び出し方法。
`src/Template/Element`というフォルダ内にファイルを作成し、

```php:
<?= $this->element( エレメント名 ) ?>
```

と記載して呼び出す。引数が必要な場合は、

```php:
<?= $this->element( エレメント名, [ キー=>値, キー=>値, キー=>値, .... ] ) ?>
```
という風に記載して、値を渡す。

### setを用いた変数設定
例えば、エレメントで使用したい変数があるとき、
①コントローラ内で変数を設定
②レイアウトテンプレートで渡された変数を、更にエレメントに渡す
といった処理をしたりする。

そのような場合、コントローラ内に、

```php:
$this->set( 変数名,　値  ) 
```

と記載する。

### 送信情報を管理する$this->request->query
「request」は、$thisに用意されているプロパティで、リクエストに関する情報を管理して、それを扱う機能がまとめられている、
queryプロパティには、クエリーテキストが連想配列としてまとめられている。

例えば`https://hogohoge/hello?name=abc`というGETリクエストがきた場合、

```php:
$this->request->query['name'];
```
をすることで、`abc`というテキストを取得することができる。

### request配列
`$this->request->query`で取得したフォームの送信情報は、他のやり方でも取得可能。
例えば、コントローラクラスには、送信された各種の情報をまとめてある「request」というメンバ変数が用意されているので、

```php:
$this->request['url']['name'];
```

と記載しても取得できる。

### request配列の一次配列一覧
用意されているものは下記。

**params**：送られてきた値を全てまとめたもの
**data**：POST送信された値
**query**：クエリーテキストで送られてきた値
**url**：送信されたアドレス
**base**：ベースのディレクトリ名
**webroot**：webrootディレクトリのアドレス
**here**：現在の場所（アドレス）

### 送信テキストのエスケープ処理
`htmlspecialchars()`メソッドで全てのHtmlタグを無効化して、ただのテキストとして処理する。
ただし、攻撃に使用されるscriptタグ以外も無効にしてしまうため、タグ表現が送れなくなってしまう。
WYSIWYGなどができなくなってしまう。

CakePHPでは、ショートハンドとして、`h()`が用意されている。


### $_GET、$_POSTは使用できない
CakePHP2.0以降使用できなくなっている。
理由は、フレームワークの仕様が変わっていく可能性があるため、$_GET、$_POSTを使用して実装してしまうと、自動生成したフォームなどへの互換性がなくなってしまうから。

### Formヘルパー
`$this->Form`という変数にインスタンスが用意されているため、これを利用する。

#### フォームの開始タグを生成する

```php:
$this->Form->create( モデル名, 属性指定 )

# 例
$this->Form->create( null,
  ['type'=>'post', 'url'=>['controller'=>'Hello', 'action'=>'index']]);
```
※同じコントローラ内のアクションを記載する場合は、`'controller'=>'Hello'`部分は不要。

#### 入力フィールドのタグを生成する

```php:
$this->Form->text( 名前 )
$this->Form->text( 名前, 属性指定 )


# 例
$this->Form->text("HelloFrom.text1");
# <input type="text" name="HelloForm[text1]"/> と出力される。
```
名前部分には、name属性で指定される名前を渡す。
この名前は、通常**モデル名.フィールド名**という形で記載する。
送信された値は、`$data[モデル名][フィールド名]`という形で、連想配列に保管される。

#### 送信ボタンのタグを生成する

```php:
$this->Form->submit( キャプション );
$this->Form->submit( キャプション, 属性指定 );


# 例
$this->Form->submit("送信");
# <div class="submit">
#   <input type="submit" value="送信"/>
# </div>                                   と出力される。
```

#### フォームの終了タグを生成する

```php:
end();
```

### GETリクエスト、POSTリクエストを判定する方法

```php:
$this->request->isGet()  # GETリクエストの場合、trueが返ってくる。 
$this->request->isPost() # POSTリクエストの場合、trueが返ってくる。
```

### Formヘルパーを利用するメリット
例えば、送信した後に再度同じページを表示された時、Formヘルパーを利用していた場合は、値が保持（入力欄にテキストが残った状態）になってくれている。
こういったちょっとした機能（けど自分で実装しようとしたらめんどくさい機能）を提供してくれる。

### モデルの種類
CakePHPには、「Model（モデル）」というクラスがあるわけではない。
**「Table（テーブル）」****「Entity（エンティティ）」**という2つのクラスを組み合わせている。

**「CakePHPでモデルを作る」**ということは
**「データベースに用意するテーブル&テーブルクラス&エンティティクラスの一式用意する」**ということ。

#### テーブルクラス
データベースのテーブルに対応するクラス。
データベースのテーブルにアクセスするためのもの。

#### エンティティクラス
取り出したデータに対応するクラス。
データの具体的な値などを管理する。

### モデルの命名規則
①データベースのテーブルは「複数形」で命名する。
②モデル（MVCの「Model」の部分）の名前は「単数形」の「キャメル記法」で命名する。
③テーブルクラスは、「複数形」＋「Table」という形の「キャメル記法」で命名する。
④エンティティクラスは、「単数形」の「キャメル記法」で命名する。
⑤スクリプトファイル名は、クラス名をそのまま（単数形のキャメル記法で）命名する。

### コントローラの命名規則
①コントローラクラス名は、コントローラ名（通常はモデル名と同じ）の「複数形」＋「Controller」という形の「キャメル記法」で命名する。
②スクリプトファイルは、コントローラクラス名をそのまま（複数形＋Controllerをキャメル記法で）命名する。
③アクションメソッド名は、一般的なメソッド名と同様、小文字で始まる「キャメル記法」で命名する。

### ビューの命名規則
①ビューを保管するフォルダ名は、コントローラ名をそのまま（複数形をキャメル記法で）命名する。
②各ビューテンプレートファイル名は、アクションメソッド名を「アンダースコア記法」にしたもので命名する。


### 命名規則の考え方
①モデル関係（モデルの名前、エンティティクラス名）は単数形、それ以外（テーブル関係など）でデータベースのデータを扱う名前は複数形にする。
②クラスとして定義されるものはキャメル記法、クラスとは直接関係しないものはアンダースコア記法にする。

### エンティティクラスの作成方法

```php:
<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class Board extends Entity {
  protected $_accessible = [
    '*' => true,
    'id' => false
  ];

}
```

エンティティクラスは、「Entitly」というクラスを継承させて作成する。
このEntityクラスには、エンティティ（データベースから取得したレコード情報をまとめたオブジェクト）に関する基本的な仕組みが組み込み済みになっている。
そのため、作成するエンティティクラスには、特になんの処理も様子する必要がない。

`$_accessible`という変数は、エンティティに用意される値への「一括代入」と呼ばれる機能を設定するためのもの。
エンティティのインスタンスを作成する際に、保管する値を一括して指定する機能。これで値を設定する際に。保護する項目（つまり値を設定できないようにする項目）を指定するのが、`$_accessible`。

上の例では、`id`に対してfalseが指定されているので、idについては一括代入ができなくなっており、それ以外は`*`の設定により、一括代入可能になっている。

### テーブルクラスの作成方法

```php:
<?php
namespace App\Model\Table;

use Cake\ORM\Table;

class BoardTable extends Table {
}
```

テーブルクラスは、「Table」というクラスを継承させて作成する。
Tableクラスに基本的な機能は用意されているため。データベースにつなげるためだけであれば何も記載しなくても良い。

### テーブルのデータを取得したい時

```php:
$this->Boards->find('all');
```
という形で取得する。
`$this`には、テーブルクラスのインスタンスがメンバ関数として追加される。
つまり上のコードでは、**Boardモデル**の**Boadrdsテーブル**にアクセスして、全てのレコードを取得している。
`find()`はレコードを検索するためのメソッドで、allを引数に渡すことで全てのレコードを取得する。

### find()で返ってくるのはQueryオブジェクト
前述した`find()`で返ってくるのは「Query」というクラスのインスタンス。
そのため、レコード情報だけではなく、様々な情報も一緒になって返ってくる。（例：各データの型情報　など）
もし「データ（レコード）を配列として取り出したい」といった場合などは、`toArray()`メソッドをしようすると、レコード情報が配列形式で取得できる。（というか整形し直される）

この辺りは下記記事とかみて、理解深めていきたい、、
[CakePHP3のORMの中核を担う「Entity」とは何か 〜CakePHP2ユーザー向けに〜](https://tech.connehito.com/entry/cakephp3-orm-entity)

Querryクラスはiteratorインターフェイスが組み込まれているため、イテレータとしてデータを順次取り出していくことができる。そうやって取り出していったものは、エンティティクラスのインスタンスになっている。

### 新たにレコード追加する際
まずはコントローラ内で空のエンティティを作成する。

```php:
$this->set('entity', $this->Boards->newEntity());
```

それをビュー側で呼び出す（Form内で呼び出す）

```php:
<?= $this->Form->create($entity, ['url'=>['action'=>'addRecord']]) ?>
```

そのあと、受け取ったコントローラ（アクションメソッド）内で保存していく。

```php:
$board = $this->Boards->newEntity($this->request->data); 
$this->Boards->save($board);
```

`newEntity()`の引数に`$this->request->data`（つまりpostリクエストで送られてきたフォームデータ）を渡すことで、フォームから送られてきた値を、各項目に保持するエンティティクラスのインスタンスが作成できる。
それをテーブルクラスの`save()`メソッドで保存する。

### findBy（）メソッド
`find()`メソッドでは複雑な検索ができるが、単純な検索であれば`findBy()`メソッドで行える。

```php:
findBy[フィールド名]（値）；

# 例：Boardテーブルのidフィールドから、id=1のレコードを検索したい場合
$this->Board->findById(1)
```
このような`findBy〜`といったメソッドのことを**「動的Finder」**と呼ぶ。

動的Finderには論理積（AND）や論理和（OR）を行うこともできる。

```php:
# idとnameフィールドの両方を検索フィールドにしたい場合（ANDの場合）
$this->Board->findByIdAndName(1, "fukuda") # idが１、かつnameがfukudaのレコードが返ってくる。

# idとnameフィールドの両方を検索フィールドにしたい場合（ORの場合）
$this->Board->findByIdOrName(1, 'fukuda') # idが１、またはnameがfukudaのレコードが返ってくる。
```

### get()メソッド
特定のid番号のレコードを取得するときに使用するメソッド。

```php:
$this->Board->get(1)
```

### Logによるログ出力について
ログを扱うために、`Log`クラスに`write`というメソッドが用意されている。

```php:
Log::write( レベル, メッセージ );
```
という風に使用する。

第一引数には、出力内容のレベルを示す値（テキスト）を用意する。


### bakeコマンド
モデルのテーブルクラス、エンティティクラス、コントローラクラス、各アクションごとのテンプレート。これらのファイルを自動生成するコマンドがbakeコマンド。

```console
bin\cake bake 対象 名前
```
とコマンド実行すれば、各ファイルを自動生成してくれる。
また対象に渡す値によって、作成するファイル範囲を変えることができる。

**all**       ：MVCで使用するもの一式全て生成する
**controller**：コントローラのみ生成する
**model**     ：モデル（エンティティとテーブル）を生成する
**template**  ：テンプレートファイルを生成する
**test**      ：ユニットテストを生成する


# 今日の一言
40％ぐらい読み終えた！
Ruby on Railsと今の所似てる（まあMVCモデルのフレームワークやから当たり前っちゃ当たり前）けど、モデルが「テーブル」と「エンティティ」の2つに分離されてるのが印象的やった！
しっかりと定義とか使い方マスターしていきたいし、池田エライザと台湾行きたい！