[CakePHP3.10]bakeとマイグレーション

config/bootstrap.php

Plugin::load('Migrations');

$ cake bake migration Create 項目1:タイプ

$ bin/cake bake migration CreateMoview title:string content:text stars:integer created:datetime
$ bin/cake bake migration CreateMovie title:string content:text stars:integer created:datetime

Creating file /home/vagrant/dev/cake/mycakeapp/config/Migrations/20220715040907_CreateMoview.php
Wrote `/home/vagrant/dev/cake/mycakeapp/config/Migrations/20220715040907_CreateMoview.php`

$ bin/cake migrations migrate
/config/Migrations/20220715040907_CreateMoview.php

$ bin/cake bake all movies
http://192.168.56.10:8080/movies

src/Model/Entity/Movie.php

    protected $_accessible = [
        'title' => true,
        'content' => true,
        'stars' => true,
        'created' => true,
    ];

src/Model/Table/MoviesTable.php

    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmptyString('id', null, 'create');

        $validator
            ->scalar('title')
            ->maxLength('title', 255)
            ->requirePresence('title', 'create')
            ->notEmptyString('title');

        $validator
            ->scalar('content')
            ->requirePresence('content', 'create')
            ->notEmptyString('content');

        $validator
            ->integer('stars')
            ->requirePresence('stars', 'create')
            ->notEmptyString('stars');

        return $validator;
    }

src/Controller/MoviesController.php

    public function index()
    {
        $movies = $this->paginate($this->Movies);

        $this->set(compact('movies'));
    }

    /**
     * View method
     *
     * @param string|null $id Movie id.
     * @return \Cake\Http\Response|null
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function view($id = null)
    {
        $movie = $this->Movies->get($id, [
            'contain' => [],
        ]);

        $this->set('movie', $movie);
    }

    /**
     * Add method
     *
     * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
     */
    public function add()
    {
        $movie = $this->Movies->newEntity();
        if ($this->request->is('post')) {
            $movie = $this->Movies->patchEntity($movie, $this->request->getData());
            if ($this->Movies->save($movie)) {
                $this->Flash->success(__('The movie has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The movie could not be saved. Please, try again.'));
        }
        $this->set(compact('movie'));
    }

    /**
     * Edit method
     *
     * @param string|null $id Movie id.
     * @return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function edit($id = null)
    {
        $movie = $this->Movies->get($id, [
            'contain' => [],
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $movie = $this->Movies->patchEntity($movie, $this->request->getData());
            if ($this->Movies->save($movie)) {
                $this->Flash->success(__('The movie has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The movie could not be saved. Please, try again.'));
        }
        $this->set(compact('movie'));
    }

    /**
     * Delete method
     *
     * @param string|null $id Movie id.
     * @return \Cake\Http\Response|null Redirects to index.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $movie = $this->Movies->get($id);
        if ($this->Movies->delete($movie)) {
            $this->Flash->success(__('The movie has been deleted.'));
        } else {
            $this->Flash->error(__('The movie could not be deleted. Please, try again.'));
        }

        return $this->redirect(['action' => 'index']);
    }

[CakePHP3.10] viewテンプレート

<p>This is People table records.</p>
<?=$this->Form->create($entity,
	['type'=>'post',
	'url'=>['controller'=>'Messages',
	'action'=>'index']]) ?>
	<fieldset class="form">
		person id
		<?= $this->Form->error('Messages.person_id'); ?>
		<?= $this->Form->text('Messages.person_id'); ?>
		Message
		<?= $this->Form->error('Messages.message'); ?>
		<?= $this->Form->text('Messages.message'); ?>
		<?= $this->Form->submit('投稿') ?>
	</fieldset>
<?=$this->Form->end() ?>

<hr>
<table>
	<thead>
		<tr><th>ID</th><th>Message</th><th>name</th><th>created at</th>
		</tr>
	</thead>
	<?php foreach($data->toArray() as $obj): ?>
	<tr>
		<td><?=h($obj->id) ?></td>
		<td><?=h($obj->message) ?></td>
		<td><?=h($obj->person->name) ?></td>
		<td><?=h($obj->created_at) ?></td>
	</tr>
	<?php endforeach; ?>
</table>
<p>This is People table records.</p>
<?=$this->Form->create(null, ["type"=>"post", "url"=>["controller"=>"People", "action"=>"index"]]) ?>
<div>find</div>
<div><?=$this->Form->text("People.find") ?></div>
<div><?=$this->Form->submit("検索") ?></div>
<?=$this->Form->end() ?>

<table>
<thead><tr>
	<th>id</th><th>name</th><th>messages</th>
</tr></thead>
<?php foreach($data->toArray() as $obj): ?>
<tr>
	<td><?=h($obj->id) ?></td>
	<td><a href="<?=$this->Url->build(["controller"=>"People", "action"=>"edit"]); ?>?id=<?=$obj->id ?>"><?=h($obj->name) ?></a></td>
	<td><?php foreach($obj->messages as $item): ?>
	"<?=h($item->message) ?>"<br>
	<?php endforeach; ?></td>
	<td><?=h($obj->age) ?></td>
	<td><a href="<?=$this->Url->build(["controller"=>"People", "action"=>"delete"]); ?>?id=<?=$obj->id ?>">delete</a></td>
</tr>
<?php endforeach; ?>
</table>

[CakePHP3.10] バリデーション2

	public function validationDefault(Validator $validator){
		$validator
			->integer('id')
			->allowEmpty('id', 'create');

		$validator
			->scalar("name")
			->requirePresence("name", "create")
			->notEmpty("name");

		$validator
			->scalar("mail")
			->allowEmpty("mail")
			->email("mail");

		$validator
			->integer("age")
			->requirePresence("age", "create")
			->notEmpty("age");

		return $validator;
	}

### エラーメッセージを個別に表示

<p><?=$msg ?></p>
<?=$this->Form->create($entity,
	['type'=>'post',
	'url'=>['controller'=>'People',
	'action'=>'add']]) ?>
<fieldset class="form">
	NAME: <?=$this->Form->error('People.name') ?>
	<?=$this->Form->text('People.name') ?>

	MAIL: <?=$this->Form->error('People.mail') ?>
	<?=$this->Form->text('People.mail') ?>
	AGE: <?=$this->Form->error('People.age') ?>
	<?=$this->Form->text('People.age') ?>
	<?=$this->Form->submit('送信') ?>
</fieldset>
<?=$this->Form->end() ?>

### 日本語のバリデーション

	public function validationDefault(Validator $validator){
		$validator
			->integer('id', "idは整数で入力下さい。")
			->allowEmpty('id', 'create');

		$validator
			->scalar("name", "テキストを入力下さい。")
			->requirePresence("name", "create")
			->notEmpty("name", "名前は必ず記入してください。");

		$validator
			->scalar("mail", "テキストを入力下さい。")
			->allowEmpty("mail")
			->email("mail", false, "メールアドレスを記入してください。");

		$validator
			->integer("age")
			->requirePresence("age", "create")
			->notEmpty("age", "必ず値を入力下さい。")
			->greaterThan('age', -1, "ゼロ以上の値を記入ください。");

		return $validator;
	}

### バリデーションメソッド
必須項目: requirePresence, notBlank, notEmpty
空白許可: allowEmpty
ASCII: ascii
数字のみ: numeric, integer, naturalNumber, nonNegativeInteger, decimal
文字と数字のみ: alphaNumeric
真偽値のみ許可: boolean
配列のみ: isArray
半角英数字: containsNonAlphaNumeric
等式: equals, notEquals
比較式: lessThan, lessThanOrEquals, greaterThan, greaterThanOrEquals
指定した範囲: range
半角数字が含まれているか: constrainsNonAlphaNumeric
等式: equal, notEquals
比較式: lessThan, lessThanOrEquals, greaterThan, greaterThanOrEquals
文字数: minLength, maxLength, lengthBetween
日時の入力: date, time, dateTime
配列に含まれるか: inList
2つの項目が同じか: sameAs
項目が含まれるか: hasField
メールアドレス: email
クレジットカード番号: creditCard
URLチェック: url
ipアドレス: ip, ipv4, ipv6
uuid: uuid

なるほど、バリデーションは主にmodelのテーブルで行うのか。controllerではないのね。

[CakePHP3.10] バリデーション

PeopleTable.php

namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\ORM\RulesChecker;
use Cake\Validation\Validator;

// 省略

	public function validationDefault(Validator $validator){
		$validator
			->integer('id')
			->allowEmpty('id', 'create');

		$validator
			->scalar("name")
			->requirePresence("name", "create")
			->notEmpty("mail");

		$validator
			->integer("age")
			->requirePresence("age", "create")
			->notEmpty("age")

		return $validator;
	}

integer(項目名), scalar(項目名): 値のタイプを指定
requirePresence(項目名, モード), フィールドの確認
allowEmpty(項目名, モード): 空を許可

なるほどー、少し理解が深まったが、まだまだ時間が足りんな…

[CakePHP3.10] 動的ファインダーとクエリビルダー

動的ファインダーは findBy${name} or findBy${name}Or${name} というような使い方をする

$data = $this->People->findByNameOrMail($find, $find);

クエリビルダーは検索条件を分けて書くことができる
演算子も使える

$data = $this->People->find()->where(["name"=>$find]);
$data = $this->People->find()->where(["name like"=>$find]);

// andWhere と orWhere
$arr = explode(",", $find);
$data = $this->People->find()->where(["age >=" => $arr[0]])
				->andWhere(['age >=' => $arr[1]])->order(["People.age"=>"asc"]);

			$data = $this->People->find()
				->order(["People.age" => "asc"])
				->order(["People.name" => "asc"])
				->limit(3)->page($find);

findの後をmodelのtableで定義しておいて、controllerの呼び出しメソッドで使えるのね。なるほど。

[CakePHP3.10] 検索

<p>This is People table records.</p>
<?=$this->Form->create(null, ["type"=>"post", "url"=>["controller"=>"People", "action"=>"index"]]) ?>
<div>find</div>
<div><?=$this->Form->text("People.find") ?></div>
<div><?=$this->Form->submit("検索") ?></div>
<?=$this->Form->end() ?>

Controller

	public function index(){
		if($this->request->is('post')){
			$find = $this->request->data['People']['find'];
			$condition = ['conditions'=>["name"=>$find]];
			$data = $this->People->find("all", $condition);
		} else {
			$data = $this->People->find("all");
		}
		$this->set('data', $data);
	}

### 曖昧検索

	public function index(){
		if($this->request->is('post')){
			$find = $this->request->data['People']['find'];
			$condition = ['conditions'=>["name like"=>$find]];
			$data = $this->People->find("all", $condition);
		} else {
			$data = $this->People->find("all");
		}
		$this->set('data', $data);
	}

決まった年齢以下の人だけ検索
$condition = [“conditions” => [“age <=" => $find]];

	public function index(){
		if($this->request->is('post')){
			$find = $this->request->data['People']['find'];
			$arr = explode(",", $find);
			$condition = ['conditions'=>[
				"and"=> [
					"age >=" => $arr[0],
					"age <=" => $arr[1]
				]
			]];
			$data = $this->People->find("all", $condition);
		} else {
			$data = $this->People->find("all");
		}
		$this->set('data', $data);
	}

### 並び順

	public function index(){
		if($this->request->is('post')){
			$find = $this->request->data['People']['find'];
			$condition = [
				'conditions'=>["name like" => $find],
				"order" => ["People.age" => "desc"]
			];
			$data = $this->People->find("all", $condition);
		} else {
			$data = $this->People->find("all");
		}
		$this->set('data', $data);
	}

### 必要な部分だけ取り出す
– 取り出す位置の設定「offset」
– 取り出すレコード数の設定「limit」
– 取り出すページの設定「page」

	public function index(){
		if($this->request->is('post')){
			$find = $this->request->data['People']['find'];
			$condition = [
				'limit'=> 3,
				"page" => $find
			];
			$data = $this->People->find("all", $condition);
		} else {
			$data = $this->People->find("all", ["order"=> ["People.age" => "asc"]]);
		}
		$this->set('data', $data);
	}

[CakePHP3.10] CRUD

### 特定のidを取得
get

	public function index(){
		$id = $this->request->query['id'];
		$data = $this->People->get($id);
		$this->set('data', $data);
	}

controller

	public function index(){
		$data = $this->People->find("all");
		$this->set('data', $data);
	}

	public function add(){
		$entity = $this->People->newEntity();
		$this->set('entity', $entity);
	}

	public function create(){
		if($this->request->is('post')){
			$data = $this->request->data['People'];
			$entity = $this->People->newEntity($data);
			$this->People->save($entity);
		}
		return $this->redirect(['action'=>'index']);
	}

### Edit

<p>This is People table records.</p>
<table>
<thead><tr>
	<th>id</th><th>name</th><th>mail</th><th>age</th>
</tr></thead>
<?php foreach($data->toArray() as $obj): ?>
<tr>
	<td><?=h($obj->id) ?></td>
	<td><a href="<?=$this->Url->build(["controller"=>"People", "action"=>"edit"]); ?>?id=<?=$obj->id ?>"><?=h($obj->name) ?></td>
	<td><?=h($obj->mail) ?></a></td>
	<td><?=h($obj->age) ?></td>
</tr>
<?php endforeach; ?>
</table>

ほう、なるほど〜

[CakePHP3.10] データベースを操作する

$ create database mydata;
$ use mydata;
$ create table people (
id int primary key AUTO_INCREMENT,
name varchar(100),
mail varchar(200),
age int
);

mysql> describe people;
+——-+————–+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+——-+————–+——+—–+———+—————-+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(100) | YES | | NULL | |
| mail | varchar(200) | YES | | NULL | |
| age | int | YES | | NULL | |
+——-+————–+——+—–+———+—————-+
4 rows in set (0.00 sec)

$ insert into people (name, mail, age) values
(“taro”, “taro@gmail.com”, 45),
(“hanako”, “hanako@gmail.com”, 34),
(“sachiko”, “sachiko@gmail.com”, 23),
(“ichiro”, “ichiro@gmail.com”, 12),
(“machiko”, “machiko@gmail.com”, 29);

app.php, app_local.phpを修正

    'Datasources' => [
        'default' => [
            'host' => 'localhost',
            /*
             * CakePHP will use the default DB port based on the driver selected
             * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
             * the following line and set the port accordingly
             */
            //'port' => 'non_standard_port_number',
            'username' => 'hoge',
            'password' => 'fuga',
            'database' => 'mydata',
            'log' => true,
            'url' => env('DATABASE_URL', null),
        ],
    ],

### エンティティとテーブル
src/Model/Entity
src/Model/Table

テーブルクラス
src/Model/Table/PeopleTable.php

namespace App\Model\Table;

use Cake\ORM\Table;

class PeopleTable extends Table {
	
}

src/Model/Entity/Person.php

namespace App\Model\Entity;

use Cake\ORM\Entity;

class Person extends Entity {
	
}

PeopleController.php

namespace AppController;

use App\Controller\AppController;

class PeopleController extends AppController {

	public function index(){
		$data = $this->People->find('all');
		$this->set('data', $data);
	}
}

src/Template/People/index.ctp

<p>This is People table records.</p>
<table>
<thead><tr>
	<th>id</th><th>name</th><th>mail</th><th>age</th>
</tr></thead>
<?php foreach($data->toArray() as $obj): ?>
<tr>
	<td><?=h($obj->id) ?></td>
	<td><?=h($obj->name) ?></td>
	<td><?=h($obj->mail) ?></td>
	<td><?=h($obj->age) ?></td>
</tr>
<?php endforeach; ?>
</table>

http://192.168.56.10:8080/people

PeopleTable.php

namespace App\Model\Table;

use Cake\ORM\Table;

class PeopleTable extends Table {
	
	public function initialize(array $config){
		parent::initialize($config);

		$this->setTable('people');
		$this->setDisplayField('name');
		$this->setPrimaryKey('id');
	}
}

Entity

class Person extends Entity {
	
	protected $_accessible = [
		'name' => true,
		'mail' => true,
		'age' => true
	];
}

なるほど、TableとEntityの使い方は何となく理解した。

[CakePHP3.10] レイアウトを作る

index.ctp

	<div class="row">
		<p>This is sample contents.</p>
		<p>これは、Helloレイアウトを利用したサンプルです。</p>	
	</div>

HelloController.php

	public function index(){

	}

### レイアウトに必要なもの
– レイアウトのテンプレート
– スタイルシートファイル
– スクリプトファイル

/webroot/css/hello.css

html {
	margin: 0px;
}
body {
	margin: 0px;
}
h1 {
	font-size: 48px;
	margin: 0px 20px;
	color: #bbf;
}
p {
	font-size: 14pt;
	color: #669;
}
.head {
	width: 100%;
	padding: 0px;
	margin: 0px;
	background-color: #f0f0ff;
}
.foot {
	position: fixed;
	bottom: 0px;
	left: 0px;
	width: 100%;
	height: 50px;
	text-align: right;
	color: #99f;
	background-color: #f0f0ff;
}
.content {
	margin: 5px 20px;
}

src/Template/Layout/hello.ctp

<!DOCTYPE html>
<html lang="en">
<head>
	<?=$this->Html->charset() ?>
	<title><?=$this->fetch("title") ?></title>
	<?=$this->Html->css("hello") ?>
	<?=$this->Html->script("hello") ?>
</head>
<body>
	<header class="head row">
		<h1><?=$this->fetch("title") ?></h1>
	</header>
	<div class="row">
		<?=$this->fetch("content") ?>
	</div>
	<footer class="foot row">
		<h5>copyright 2022 hpscript</h5>
	</footer>
</body>
</html>

HelloController.php

	public function initialize(){
		$this->viewBuilder()->setLayout("hello");
	}

Template/Element/header.ctp

<h1 style="text-align:right; font-size:30px; margin: 0px 20px;">
	title: <?=$this->fetch('title') ?>.
</h1>
<h2 style="text-align:right; font-size:16pt; margin:0px 20px; color:#ccf;">
	~ <?=$subtitle ?> ~
</h2>

Template/Element/footer.ctp

なるほどー、共通レイアウトはLayoutフォルダで、パーツはElementフォルダで、メソッドの画面は ${ControllerName}画面で構築していくのね
レイアウト周りの全体構造は理解した

[CakePHP3.10] フォームヘルパーの使い方

### チェックボックス/ラジオボタン

			<?=$this->Form->create(null, ["type"=>"post", "url"=>["controller"=>"Hello", "action"=>"index"]])?>
				<input type="hidden" name="_csrfToken" value="<?= $this->request->getParam('_csrfToken') ?>">
				<tr><th>CheckBox</th><td><?=$this->Form->checkbox("Form1.check", ["id"=>"check1"]) ?>
					<?=$this->Form->label("check1", "check box") ?>
				</td></tr>
				<tr><th>RadioButton</th><td><?=$this->Form->radio("Form1.radio", [
						["text"=>"mail", "value"=>"男性", "checked"=>true],
						["text"=>"femail","value"=>"女性"]
					]) ?></td></tr>
				<tr><th></th><td><?=$this->Form->submit("送信") ?></td></tr>
			<?=$this->Form->end() ?>

### 選択リスト

			<?=$this->Form->create(null, ["type"=>"post", "url"=>["controller"=>"Hello", "action"=>"index"]])?>
				<input type="hidden" name="_csrfToken" value="<?= $this->request->getParam('_csrfToken') ?>">
				<tr><th>Select</th><td><?=$this->Form->select("Form1.select",
				 	["one"=>"最初","two"=>"真ん中","three"=>"最後"]) ?>
				</td></tr>
				<tr><th></th><td><?=$this->Form->submit("送信") ?></td></tr>
			<?=$this->Form->end() ?>

### 複数項目の選択

			<?=$this->Form->create(null, ["type"=>"post", "url"=>["controller"=>"Hello", "action"=>"index"]])?>
				<input type="hidden" name="_csrfToken" value="<?= $this->request->getParam('_csrfToken') ?>">
				<tr><th>Select</th><td><?=$this->Form->select("Form1.select",
				 	["one"=>"最初","two"=>"2番目","three"=>"真ん中","four"=>"4番目","five"=>"最後"],
				 	["multiple"=>true, "size"=>5]) ?>
				</td></tr>
				<tr><th></th><td><?=$this->Form->submit("送信") ?></td></tr>
			<?=$this->Form->end() ?>