Cakephp4へのバージョンアップ

# Cakephp2からCakephp4へバージョンアップメモ
　かなりカスタマイズされたCakephp2をとうとうCakephp4にバージョンアップする事になり。
　ちまちま調べながら、まとめて知りたいことを書き連ねてます。
　追記追記していく所存。

### フォルダー構成メモ
|cakephp2       | →　|Cakephp4|
|:-:|:-:|:-:|
|app/Config| → |config|
|app/Contoroller| → |src/Controller|
|app/Model| → |src/Model|
|app/View/Helper| → |src/View/Helper|
|app/View| → |templates|


## まずはControllerで知りたかった事の一部抜粋。
　Findやデータのわたしかたなどなど
### Cakephp2で書いてた方法
```php:WorkController.php
  //社内便利コンポーネントの呼び込み
   $components=array('CommonOriginal', 
                array('model'=>'Work',
                'index'=>array('hogehoge'・・・);////省略

	public function edit($id=null){
        //ログイン者の情報も必要なので取得。
		$user = $this->Auth->user();
		
		$status = $this->UserStatus->getXXStatus($user['id']);
        //データの取り出しJoinしまくり
		if($status==1){
		//　省略
		}

		$WorkCheck = $this->Work->findById($id);
		$data = $this->Work->find('all',
						array(
						'fields'=>Hash::merge(
								$this->Work->getDataSource()->fields($this->Work),
								$this->Time->getDataSource()->fields($this->Time),
								array('TotalTime.target_sum_time','TotalTime.fix'	)
								),
						'conditions'=>array('Work.id'=>$id)
						,'joins'=>
									array('type'=>'LEFT OUTER',
										'alias'=>'Time',
										'table'=> $this->Time->useTable ,
										'conditions'=>array('Time.id = Work.time_id',
															'Time.user_id = Work.user_id')
									),
									array('type'=>'LEFT OUTER',
										'alias'=>'TotalTime',
										'table'=> $this->TotalTime->useTable ,
										'conditions'=>array('TotalTime.id = Work.total_time_id',
															'TotalTime.user_id = Work.user_id')
									),
							)
							)
						);
		//一覧
		if( !empty($data[0]['Work']['order_number']) ||  !empty($data[0]['Work']['dummy'])  ){
			$TotalTimes = $this->TotalTime->TotalTImelist($user['id'] ,$data[0]['Work']['order_number'],$data[0]['Work']['dummy']);
			$this->set('TotalTimes',$TotalTimes);
		}

		$Attendance = $this->Attendance->getXXX($user['id'],
							( empty($data[0]['WorkMeter']['start_day']) ? null : date("Y/m/d" , strtotime($data[0]['WorkMeter']['start_day']) ))
							);
		//休憩合計時間
		$sumBreak = $this->WorkBreak->SumWorkMeterBreak($id);
		$time_now = strtotime( date( "Y/m/d H:i:s", time() ));

       //省略

	}
```

### Cakephp4にとりあえず直したソース。
モデルが複数形になったら、コントローラー名も複数形。
```diff_php:WorksController.php
//↓↓↓↓宣言しないといけない↓↓↓↓↓↓↓↓
+ use Cake\ORM\TableRegistry;
+ use Cake\Utility\Hash; //ハッシュを使うために必要
//↑↑↑↑宣言しないといけない↑↑↑↑↑↑↑↑

  public function initialize(): void
  {
    parent::initialize();
  //社内便利コンポーネントの呼び込み
+   $this->loadComponent('CommonOriginal', 
+                ['model'=>'Works',
+               'index'=>['hogehoge'・・・);////省略
   }

	public function edit($id=null)
    {
        //ログイン者の情報も必要なので取得。
+		$user = $this->request->getAttribute('identity');
		
+		$status = $this->fetchTable('UserStatus')->getXXStatus($user->id);
		if($status==1)
        {
		   //省略
		}

+		$WorkCheck = $this->Works->get($id);
+        $querydata = $this->Works->find()
+                                ->select(
                                    Hash::merge(
+                                           $this->Works->getSchema()->columns(),
+                                           $this->fetchTable('Times')->getColumns('Times') ,
                                           ['TotalTimes.target_sum_time','TotalTimes.fix']
                                ))
+                                ->where(['Works.id'=>$id])
+                                ->join([
+                                      'Times'=>['type'=>'LEFT OUTER',
+                                                'table'=> $this->fetchTable('Times')->getTable(),
                                       　        'conditions'=>['Times.id = Works.time_id',
                                                                'Times.user_id = Works.user_id']
                                                ],
                                       'TotalTimes'=>['type'=>'LEFT OUTER',
+                                                'table'=> $this->fetchTable('TotalTimes')->getTable()  ,
                                       　         'conditions'=>['TotalTimes.id = Works.total_time_id',
                                                                'TotalTimes.user_id = Works.user_id']
                                                ],
                                       ]);
+        $data= $querydata->toList();
        
		//一覧
+        if(!empty($data[0]->order_number) || !empty($data[0]->dummy))
        {
+            $TotalTimes = $this->fetchTable('TotalTimes')->TotalTImelist($user->id ,$data[0]->order_number,$data[0]->dummy);
            $this->set('TotalTimes',$TotalTimes);
        }

+        $Attendance =$this->fetchTable('Attendances')->getXXX($user['id'],
+                                ( empty($data[0]->start_day) ? null :$data[0]->start_day->i18nFormat('yyyy/MM/dd') )
                                );
        //休憩合計時間
+        $sumBreak = $this->fetchTable('WorkBreaks')->SumWorkMeterBreak($id);
        $time_now = strtotime( date( "Y/m/d H:i:s", time() ));

		//省略

	}

```
大きく書き方が変わったなという場所は色付けしました。そして気を付けることは
* テーブル名は複数形！
* コントローラーと違うModelを使う際には、書き方を変える。
* 宣言をしないと使えないものがある。
* FindAllしたものはコントローラー内で見れなかったので、toList()しないといけないらしい。
* 詳細ページのデフォルトfunctionが、showからviewに変わった。

ていうか書き方変わりすぎて辛い。
#### コントローラの差分メモ

<table>
<thead><tr>
<th>  </th>
 <th> cake2 </th>
 <th> cake4 </th>
 <th>備考</th>
</tr>
</thead>
<tr>
<td> Component</td>
 <td> $components=array('コンポーネント名', 引数);</td>
 <td>$this->loadComponent('コンポーネント名', 引数);  </td>
 <td> $components廃止だそうです</td>
</tr>
<tr>
<td> モデルの呼び出し  </td>
 <td> $this->model  </td>
 <td> $this->models  </td>
 <td>Cakephp4ではモデル名は複数形にする</td>
</tr>
<tr>
<td> モデルの呼び出し２  </td>
 <td> $this->model  </td>
 <td> $this->fetchTable('models')  </td>
 <td>Cakephp2でやれた方法は出来ないのか不明。</td>
</tr>
<tr>
<td> カラム名一気に取得  </td>
 <td> \$this->model->getDataSource()->fields($this->model)  </td>
 <td> $this->models->getSchema()->columns() </td>
 <td></td>
</tr>
<tr>
<td> カラム名一気に取得２ </td>
 <td> \$this->model->getDataSource()->fields($this->model)    </td>
 <td> $this->fetchTable('models')->getColumns('models')  </td>
 <td>なかなかソースが見つからず大変だった</td>
<tr>
<td> モデルのFind  </td>
 <td> \$this->model->findById($id)  </td>
 <td> \$this->models->get($id)  </td>
 <td>Get好きよねCakephp4って</td>
</tr>
<tr>
<td> 日付フォーマット</td>
 <td> date("Y/m/d" , strtotime($data['model']['start_day'])  </td>
 <td> $data->start_day->i18nFormat('yyyy/MM/dd')   </td>
 <td>整形するための指示方法がちょっと違うので注意</td>
</tr>
<tr>
<td> データの持ち方</td>
 <td> \$data['model']['name']</td>
 <td>\$data->name   </td>
 <td> メインで呼び出したModel名は省かれる。</td>
</tr>
<tr>
<td> Findのジョイン</td>
 <td> array(省略,
 'joins'=>array(<br />
    'type'=>'LEFT OUTER'
    ,'alias'=>Table名
    …省略　</td>
 <td>
(省略)
->join([Table名=>[
      'type'=>'LEFT OUTER'
     ,…省略] </td>
 <td> なんで単数形になったの？
   そしてエイリアス名の配列位置が変わった</td>
</tr>
<tr>
<td>Begin宣言</td>
 <td>$this->{model}->begin;</td>
 <td>//ヘッダーの宣言　

use Cake\Datasource\ConnectionManager;
use Cake\Core\Exception\Exception;

//ソース内部
\\$connection = ConnectionManager::get('default');
\$connection->begin();</td>
 <td> モデル名で宣言できなくなったので注意。/td>
</tr>
<tr>
<td> ログ出力</td>
 <td> \$this->log($変数名, 'debug');</td>
 <td>\$this->log(print_r($変数名,true), 'debug'); </td>
 <td> print_rしないと連想配列が出力できない。Modelクラスでは出し方違うので注意</td>
</tr>
</table>



## Component　改修迷走中
　退職された人が作った、便利共通コンポーネントを愛用していたんですが。やはりこれもバージョンアップ。これを改修していってます（途中）

### Cakephp2での表記
 よくわかんないけど、コントローラーからの引数とかコントローラ名を取得してセットしてる
```php:CommonOriginalComponent.php
class CommonOriginalComponent extends Component {

	public $settings = array();

	public $model;

	public function __construct(ComponentCollection $collection, $settings = array()) {
		$settings = array_merge($this->settings, (array)$settings);
		$this->Controller = $collection->getController();
		parent::__construct($collection, $settings);
	}

    public function index($fastView = null)
    {
  		$query = Hash::filter($this->Controller->params->query);
		$fastView = empty($fastView ) ? isset($this->Controller->request->params['named']['page']) : !empty($fastView) ;
		
	    $this->Controller->request->data = $query;
        //省略
		if(empty($options['limit'] )){
			$options['limit'] = 10;
		}
		if($fastView){
	    	$this->paginateSet($options);
	    }
	}

  /*Paginatorのセット*/
    public function paginateSet($options,$datasetName ='data')
    {
　　　　//検索条件を入れたやつをPaginatorのsettingsにセット
    	$this->Controller->Paginator->settings = array_merge($this->Controller->Paginator->settings, $options);
    	$whitelist = isset($this->settings['index']['whitelist']) ? $this->settings['index']['whitelist'] : array();
    	try{
			$data = $this->Controller->Paginator->paginate($this->settings['model'], array(), $whitelist);
		} catch(NotFoundException $e){
			//PagenaiteでNotFoundExceptionになった場合強制的に１ページ目に移動
			$this->Controller->request->params['named'] = array('page' => 1);
			$data = $this->Controller->Paginator->paginate($this->settings['model'], array(), $whitelist);
		}
		$this->Controller->set($datasetName, $data);
	}


}
```
### Cakephp4での表記
 __constructは使わないほうがいいらしく。Cakephp2で使われてる奴の流用だとわかったので調べたけど、よくわからず。うまく取れる場所はbeforeFilterでは？と直してみました。

```diff_php:CommonOriginalComponent.php

namespace App\Controller\Component;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
：//省略
use Cake\Http\ServerRequest;
use Cake\Utility\Hash;
class CommonOriginalComponent extends Component {

  public function beforeFilter()
    {
       $this->Controller = $this->getController();
+      $this->request = $this->Controller->getRequest();
+      $this->settings = $this->Controller->components['CommonOriginal'];

    }
  public function index($fastView = null)
  {
+		$query = Hash::filter($this->Controller->getRequest()->getQueryParams());//検索条件の取得
+		$fastView = empty($fastView ) ? !empty($this->Controller->getRequest->getQuery('page')) : !empty($fastView) ;
        //省略
        $this->paginate['limit'] = (!empty($options['limit']) ? $options['limit'] : $this->paginate['limit'] );

        if($fastView){
            $this->paginateSet($options,$datasetName);
        }
  }


  /*Paginatorのセット*/
  public function paginateSet($options,$datasetName ='data')
  {
      //whitelistは廃止された allowedParametersを使えって書いてあるけど、使い方が分からず保留
      //$whitelist = isset($this->settings['index']['whitelist']) ? $this->settings['index']['whitelist'] : array();
      try{
         //Paginatorのsettingsがなくなってしまい。
         //とりあえず、検索条件を入れたやつでQueryを再度つくってみたけどなんか違う気がするので保留中です         
          $query = $this->Controller->$datasetName->find('all') //~省略~
+         $data = $this->Controller->paginate($query,$this->paginate);
      } catch(NotFoundException $e){
        //PagenaiteでNotFoundExceptionになった場合強制的に１ページ目に移動
        $this->paginate=['page' => 1];
        $this->Controller->Paginator->paginate($this->settings['model'], $options);
        $data = $this->Controller->paginate($query,$this->paginate);
      }
      $this->Controller->set("{$datasetName}", $data);
 }


```

どれがどう変わったのか、というよりも元のソースの理解が半分もないので新しくするためにどれを使えばいいのか…。

#### コンポーネントの差分メモ

|  | cake2 | cake4 |備考|
|:-:|:-:|:-:|:-:|
| 渡された引数  | $settings | $this->Controller->components['CommonOriginal'] |Cakephp2はよくわからん|
| Formからのパラメータ  |$this->Controller->params->query | $this->Controller->getRequest()->getQueryParams()|Cakephp4のリクエストをとりあえず全部ログに出した結果これだった|


#### モデルの差分メモ

<table>
<thead><tr>
<th>  </th>
 <th> cake2 </th>
 <th> cake4 </th>
 <th>備考</th>
</tr>
</thead>
<tr>
<td> ログ出力</td>
 <td> \$this->log($変数名, 'debug');</td>
 <td>\Cake\Log\Log::write('debug', print_r($変数名,true)); </td>
 <td> print_rしないと連想配列が出力できない</td>
</tr>
</table>