CakePHP3・4のネットでは見つからないちょっとした実装集

CakePHPでネットでは見つからないちょっとした実装集

31 回閲覧されました

みなさんこんにちは、jonioです。

CakePHPに関して検索しても見つけることができなくて苦労して実装した実装集を掲載します。

情報が見つかり次第追記します。

アソシエーション時に条件を満たしたら削除ボタンを表示

アイドル会社1とアイドル会社2があってそれぞれの会社に部署1と部署2があり部署1と部署2にはそれぞれ従業員が3人いたとします。

会社のカラムは下記になります。

部署のカラムは下記になります。

従業員のカラムは下記になります。

会社はcompaniesテーブル、部署はdepartmentsテーブル、従業員はusersテーブルに情報が保存されているとします。

アソシエーションの関係は以下になります。

  • 従業員(usersテーブル) :companiesテーブルとdepartmentsテーブルに対して子テーブル
  • 部署(departmentsテーブル) : companiesテーブルに対して子テーブル
  • 会社(companiesテーブル) : departmentsテーブルとusersテーブルに対して親テーブル

アソシエーションの設定はしたとします。

viewアクション

「http://localhost/companies/view/1」にアクセスした時に下記の表示にするのを目標にします。

会社の中で部署に紐づいているユーザーがいますが部署に紐づくユーザーがいなくなったら削除ボタンが表示されるようにします。

Companiesコントローラーのviewアクションのコードは下記になります。

public function view($id = null)
{
  $company = $this->Companies->get($id, [
    'contain' => ['Users'],
  ]);

  $companyId = $this->Auth->user('company_id');

  $departments = $this->Companies->Departments->find()
                                              ->where(['company_id' => $companyId])
                                              ->contain(['Users'])
                                              ->all();

  $this->set(compact('company', 'departments'));
}

今回の実装に関係あるのは7行目〜12行目です。

会社に紐づく部署を考えるので9行目の「->Companies->Departments」があります。

実装に使う会社はユーザーがログインしている会社にしないといけないので会社idを7行目で取得して10行目で使っています。

これだけだとユーザーが考慮されていないのでユーザーも含めます。

ユーザーがログインしている会社の中のユーザーを使うので11行目があります。

companiesテーブルに紐づくdepartmentsテーブルに紐づくユーザーが取得できているかを確認します。

viewアクションの中に下記の記述をします。

public function view($id = null)
{
  $company = $this->Companies->get($id, [
    'contain' => ['Users'],
  ]);

  $companyId = $this->Auth->user('company_id');

  $departments = $this->Companies->Departments->find()
                                              ->where(['company_id' => $companyId])
                                              ->contain(['Users'])
                                              ->all();

  dd($departments);        //←この行を追加
  
  $this->set(compact('company', 'departments'));
}

すると会社の部署に紐づいているユーザーがいる場合は下記の表示になります。

会社の部署に紐づいているユーザーがいない場合は下記の表示になります。

viewアクションで表示するビューのコードを下記にします。

<div class="related">
  <h4><?= __('部署') ?></h4>
  <div class="table-responsive">
    <table>
       <tr>
         <th><?= __('Name') ?></th>
         <th class="actions"><?= __('Actions') ?></th>
       </tr>
       <?php foreach ($departments as $department) : ?>
         <tr>
           <td><?= h($department->name) ?></td>
           <td class="actions">
             <?= $this->Html->link(__('View'), ['controller' => 'Departments', 'action' => 'view', $department->id]) ?>
             <?= $this->Html->link(__('Edit'), ['controller' => 'Departments', 'action' => 'edit', $department->id]) ?>
             <?php if(count($department->users) == 0): ?>
               <?= $this->Form->postLink(__('Delete'), ['controller' => 'Departments', 'action' => 'delete', $department->id], ['confirm' => __('Are you sure you want to delete # {0}?', $department->id)]) ?>
             <?php endif; ?>
           </td>
         </tr>
       <?php endforeach; ?>
     </table>
   </div>
</div>

部署に紐づく従業員が0人の時のみ削除ボタンを表示させているのが15行目です。

viewアクションで「dd($departments);」をして部署に紐づくユーザーを取得するのは「$department->users」です。(usersは下記参照)

これで実装が完成です。

1つのカラムを登録すると他のカラムも自動的に登録される

例えばusersテーブルにnameカラムとemailカラムとpasswordカラムがあったとします。

postsテーブルに紐づくビューでusersテーブルのnameカラムのデータをリストで表示させて名前を選択して登録すると自動的に名前に紐づいているemailカラム・passwordカラムもpostsテーブルに登録させたいみたいな時に使えます。

簡潔にすると「レコードの1つのカラムを登録すると他のカラムが自動的に登録される」と言えます。

今回はnameカラムを登録した時にemailカラムも登録できるようにします。

allコマンドでモデル・ビュー・コントローラーは作成したのを前提に話を進めます。

ユーザーの登録(usersテーブル)はコントローラー・ビュー共にデフォルトのまま使っているのでコードの掲載はしません。

テーブル

usersテーブルは下記です、nameカラム・emailカラム・passwordカラムがありますがnameカラムとemailカラムを使います。

postsテーブルは下記です、titleカラム・contentカラムがあります。

ビュー

コードを下記にします。

<div class="row">
    <aside class="column">
        <div class="side-nav">
            <h4 class="heading"><?= __('Actions') ?></h4>
            <?= $this->Html->link(__('List Posts'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
        </div>
    </aside>
    <div class="column-responsive column-80">
        <div class="posts form content">
            <?= $this->Form->create() ?>
            <fieldset>
                <legend><?= __('Add Post') ?></legend>
                <?= $this->Form->select('title', ['options' => $users]) ?>
            </fieldset>
            <?= $this->Form->button(__('Submit')) ?>
            <?= $this->Form->end() ?>
        </div>
    </div>
</div>

13行目が登録フォームです、「’options’ => $users」の$usersはコントローラーから情報を渡します。

コントローラー

コードを下記にします。

public function add()
{
  $this->loadModel('Users');

  $users = $this->Users
                ->find('list', ['keyField' => 'name', 'valueField' => 'name'])
                ->toArray();
    
  $post = $this->Posts->newEmptyEntity();
  
  if ($this->request->is('post')) {
    $postData = $this->request->getData();
    $user = $this->Users->find()->where(['name' => $postData['title']])->first();
            
    if ($user) {
      $postData['content'] = $user->email;
      $post = $this->Posts->patchEntity($post, $postData);
      if ($this->Posts->save($post)) {
        $this->Flash->success(__('The post has been saved.'));
        return $this->redirect(['action' => 'index']);
      } else {
        $this->Flash->error(__('The post could not be saved. Please, try again.'));
      }  
    }

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

5行目の「[‘keyField’ => ‘name‘, ‘valueField’ => ‘name‘]」は「KeyField」が連想配列のkeyに当たりValueFieldが連想配列のvalueに当たります。

keyとvalueに「name」を使っていますがusersテーブルのnameカラムになります、存在しないカラム名を使うとエラーになります。

valueFieldのnameが登録フォームに表示される名前でkeyFieldのnameが登録フォームで名前を選択して登録した時にテーブルに保存される値です。

本当に値が入っているかを確認します。

dd関数を使います。

public function add()
{
  $this->loadModel('Users');

  $users = $this->Users
                ->find('list', ['keyField' => 'name', 'valueField' => 'name'])
                ->toArray();
  
  dd($users);     //←この行を追加


登録画面をリロードすると下記の表示になります。

//左の項目がkeyFieldで右の項目がvalueField
[
'jonio' => 'jonio',
'jonio2' => 'jonio2',
]

右の項目が登録フォームに表示される名前で左の項目が実際に登録される値です。

カラム名を2つともnameにして見にくいですが表示される名前と登録される名前が同じの方が対応がわかりやすいと思いわざと同じにしています。

コントローラーのコードに話を戻して登録フォームで名前を選択して登録した時にpostsテーブルのtitleカラムにusersテーブルのnameカラムの値が登録されているかを確認します。

dd関数を使います。

public function add()
{
  $this->loadModel('Users');

  $users = $this->Users
                ->find('list', ['keyField' => 'name', 'valueField' => 'name'])
                ->toArray();
    
  $post = $this->Posts->newEmptyEntity();
  
  if ($this->request->is('post')) {
    $postData = $this->request->getData();
    dd($postData);               //←を追加

登録フォームで名前を選択して登録すると下記の表示になります。

//名前の選択でjonio2を選択しています

[
'title' => 'jonio2',
]

「dd($postData)」ではなく「dd($postData[‘title’])」にするとtitleカラムの値(jonio2)を取得できているのを確認できます。

話をまたコントローラーのコードに戻して今のままではcontentカラムに値を登録することができませんがそれをできるようにする媒体みたいな物が13行目です。

これがあることで登録フォームで選択した名前(今回の例だとjonio2)があるレコードを取得しています。

ちなみに「where([‘name’ => $postData[‘title’]])」で登録レコードを選択して「$postData[‘title’]」でjonio2が取得できます。

今からcontentカラムにemailを登録しますが13行目の$userに何が入っているかをdd関数で見てみます。

public function add()
{
  $this->loadModel('Users');

  $users = $this->Users
                ->find('list', ['keyField' => 'name', 'valueField' => 'name'])
                ->toArray();
    
  $post = $this->Posts->newEmptyEntity();
  
  if ($this->request->is('post')) {
    $postData = $this->request->getData();
    $user = $this->Users->find()->where(['name' => $postData['title']])->first();
    dd($user);             //←この行

するとemailカラムの値を含んだオブジェクトが取得できます。

object(App\Model\Entity\User) id:0 {
'id' => (int) 2
'name' => 'jonio2'
'email' => 'b@demo.com'
'password' => 'xxxxxxx'
'[new]' => false
'[accessible]' => [ ]
'[dirty]' => [ ]
'[original]' => [ ]
'[virtual]' => [ ]
'[hasErrors]' => false
'[errors]' => [ ]
'[invalid]' => [ ]
'[repository]' => 'Users'
}

よって16行目の「$user->email」でjonio2に紐づいたemailカラムの値を取得してpostsテーブルのcontentカラム($postData[‘content’])に登録します。

あとはコードを読めば分かると思います。