こんにちは!
スマレジの テックファーム(SES 部門)のWebエンジニア やまて(@r_yamate) と申します。
はじめに
前回の記事(④登録機能の作成)で、商品データを Products テーブルに登録する、登録機能を作成しました。
本連載の目次
本連載では以下の順序で進めています。
- ① Docker での開発環境構築
- ② Zend Framework のインストール
- ③ 一覧画面の作成
- ④ 登録機能の作成
- ⑤ 編集・削除機能の作成(今回)
- ⑥ バリデーションメッセージの日本語化
目次
今回は、登録済みの商品データを編集・削除する機能を作成します。
これで、読込、登録、編集、削除のCRUD機能が揃います。
Zend Framework公式ドキュメントのチュートリアルを参考に作成しました。(ところどころアレンジしています)
本記事完了時点のソースコード
本記事完了時点のソースコードをGitHub上に公開しています。
以下は変更差分です。
商品管理ページの構成
ページ | 説明 |
---|---|
商品一覧 | 商品の一覧を表示。商品の新規登録や編集、削除のためのリンクボタン表示。 |
商品登録 | 新規商品を登録するためのフォーム。 |
商品編集⭐️ | 商品を編集するためのフォーム。 |
商品削除⭐️ | 商品を削除することを確認し、削除するページ。 |
1. 編集機能の作成
商品データを編集する機能を作成します。
1-1. コントローラの編集( editAction()
メソッドの追記)
フォームからデータを取得し、対象の商品データの行を編集し、商品一覧にリダイレクトするメソッドを追記します。
編集:module/Product/src/Controller/ProductController.php
<?php namespace Product\Controller; use Product\Form\ProductForm; use Product\Model\Product; use Product\Model\ProductTable; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; /** * 商品コントローラークラス。 */ class ProductController extends AbstractActionController { // 略 /** * 商品を編集する。 * フォームからデータを取得し、対象の商品の行を編集し、商品一覧にリダイレクトする。 * * @return Response|array * @throws Exception\DomainException */ public function editAction() { // マッチしたルートからパラメータを取得(module/Product/config/module.config.php 内に作成したルートから id を取得)する。 $id = (int) $this->params()->fromRoute('id', 0); // id が 0 なら、編集フォームにリダイレクトする。 if (0 === $id) { return $this->redirect()->toRoute('product', ['action' => 'add']); } // 指定されたidの商品を取得する。商品が見つからない場合は例外(リダイレクトされる)。 try { $product = $this->table->getProduct($id); } catch (\Exception $e) { return $this->redirect()->toRoute('product', ['action' => 'index']); } $form = new ProductForm(); $form->bind($product); // bind()メソッドは、モデルをフォームにアタッチする。 $form->get('submit')->setAttribute('value', '編集'); $request = $this->getRequest(); $viewData = ['id' => $id, 'form' => $form]; if (!$request->isPost()) { return $viewData; } $form->setInputFilter($product->getInputFilter()); $form->setData($request->getPost()); // バリデーション失敗の場合、フォームを再表示する(バリデーション失敗の内容を、ビューレイヤーに伝える)。 if (!$form->isValid()) { return $viewData; } // フォームからデータを取得し、対象の商品の行を編集する。 $this->table->editProduct($product); // 商品一覧にリダイレクトする。 return $this->redirect()->toRoute('product', ['action' => 'index']); } }
1-2. モデルの編集①(モデルをフォームにアタッチ)
Product ファイルを編集します。
編集:/module/Product/src/Model/Product.php
<?php // 略 /** * 商品モデルクラス。 * zend-db の TableGateway クラスで動作させるために、 exchangeArray() メソッドを実装する。 */ class Product implements InputFilterAwareInterface { // 略 /** * エンティティのプロパティを配列としてコピーして取得する。 * * モデルをフォームにアタッチし、以下2つに使われる。 * - フォームを表示するときの各要素の初期値をモデルから抽出、 * - isValid()でバリデーションに成功すると、フォームのデータをモデルに戻す * * @return array エンティティのプロパティを配列にコピーしたもの */ public function getArrayCopy() { return [ 'id' => $this->id, 'item_name' => $this->itemName, 'price' => $this->price, 'image' => $this->image, ]; } // 略 }
上記のPHPDocにも記述していますが、モデルをフォームにアタッチして、使用するのは「フォームを表示するときの各要素の初期値をモデルから抽出」「isValid()
でバリデーションに成功すると、フォームのデータをモデルに戻す」時です。
1-3. モデルの編集②(商品データのテーブルからの取得、編集)
products テーブルからのデータ取得、テーブルへの編集処理をする ProductTable ファイルを編集します。
編集:module/Product/src/Model/ProductTable.php
<?php namespace Product\Model; use Zend\Db\TableGateway\TableGatewayInterface; /** * 商品のデータベース・テーブルに対する操作を実行するクラス。 */ class ProductTable { // 略 /** * 一行を Product オブジェクトとして取得する。 * * @param string $id 商品ID * @return */ public function getProduct(string $id) { return $this->tableGateway->select(['id' => $id])->current(); } /** * データベースの既に存在する行を更新する。 * * @param Product $product 既に存在する商品のカラム * @return void */ public function editProduct(Product $product) { $data = [ 'item_name' => $product->itemName, 'price' => $product->price, 'image' => $product->image, ]; $product = $this->getProduct($product->id); if (isset($product)) { $this->tableGateway->update($data, ['id' => $product->id]); } } }
1-4. ビューの編集(編集用のフォーム画面の作成)
商品一覧画面にフォーム画面へ遷移する「編集」ボタンを設置します。「削除」ボタンも併せて設置しておきます。
編集:module/Product/view/product/product/index.phtml
<!-- 略 --> <table class="table"> <tr> <th>ID</th> <th>商品名</th> <th>商品単価</th> <th> </th> </tr> <!-- コントローラアクションから割り当てた$productsを繰り返し処理 --> <?php foreach ($products as $product) : ?> <tr> <!-- 各アルバムのタイトルとアーティストを表示するテーブルを作成 --> <!-- ビュースクリプトに渡す変数と内部で作成した変数を区別するために、 $this->{variable name} を使用してアクセスする --> <td><?= $this->escapeHtml($product->id) ?></td> <td><?= $this->escapeHtml($product->itemName) ?></td> <td><?= $this->escapeHtml(number_format($product->price)) ?></td> <!-- レコードの編集と削除を可能にするためのリンクを提供 --> <td> <!-- url() ビューヘルパーの使用 --> <a href="<?= $this->url('product', ['action' => 'edit', 'id' => $product->id]) ?>">編集</a> <a href="<?= $this->url('product', ['action' => 'delete', 'id' => $product->id]) ?>">削除</a> </td> </tr> <?php endforeach; ?> </table>
続いて、商品データの編集するためのフォーム画面(ビュースクリプト)を作成します。
作成・編集:module/Product/view/product/product/edit.phtml
<?php $title = '商品の編集'; $this->headTitle($title); ?> <h1><?= $this->escapeHtml($title) ?></h1> <?php // 要素にデフォルトの CSS クラスとプレースホルダーテキストを提供する。 $itemName = $form->get('item_name'); $itemName->setAttribute('class', 'form-control'); $itemName->setAttribute('placeholder', '商品名'); $price = $form->get('price'); $price->setAttribute('class', 'form-control'); $price->setAttribute('placeholder', '商品単価'); // 送信ボタン用の CSS クラスを提供する。 $submit = $form->get('submit'); $submit->setAttribute('class', 'btn btn-primary'); $form->setAttribute('action', $this->url('product', [ 'action' => 'edit', 'id' => $id, ])); $form->prepare(); echo $this->form()->openTag($form); ?> <!-- フォームグループの div で要素をラップし、その中でラベル、要素、エラーを別々にレンダリングする。 --> <div class="form-group"> <?= $this->formLabel($itemName) ?> <?= $this->formElement($itemName) ?> <span class="text-danger"><?= $this->formElementErrors()->render($itemName, ['class' => 'help-block']) ?></span> </div> <div class="form-group"> <?= $this->formLabel($price) ?> <?= $this->formElement($price) ?> <span class="text-danger"><?= $this->formElementErrors()->render($price, ['class' => 'help-block']) ?></span> </div> <?php echo $this->formSubmit($submit); echo $this->formHidden($form->get('id')); echo $this->form()->closeTag();
1-5. 機能の確認(編集してみる)
下記URLにアクセスします。
「日替わりランチC」を編集してみます。
http://localhost/product/edit/3
商品名、商品単価を変更して、編集ボタンを押します。
編集されました!
2. 削除機能の作成
商品データを削除する機能を作成します。
2-1. コントローラの編集( deleteAction()
メソッドの追記)
編集:module/Product/src/Controller/ProductController.php
<?php namespace Product\Controller; use Product\Form\ProductForm; use Product\Model\Product; use Product\Model\ProductTable; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; /** * 商品コントローラークラス。 */ class ProductController extends AbstractActionController { // 略 /** * 商品を削除する。 * ユーザーが削除をクリックしたときに確認フォームを表示し、「はい」をクリックしたら削除を実行する。 * * @return Response|array * @throws Exception\DomainException */ public function deleteAction() { // マッチしたルートからパラメータを取得(module/Product/config/module.config.php 内に作成したルートから id を取得) $id = (int) $this->params()->fromRoute('id', 0); if (!$id) { return $this->redirect()->toRoute('product'); } $request = $this->getRequest(); // isPost() をチェックして、確認ページを表示するか、アルバムを削除するかを決定する。 if ($request->isPost()) { $del = $request->getPost('del', 'いいえ'); if ($del === 'はい') { $id = (int) $request->getPost('id'); $this->table->deleteProduct($id); } // 商品一覧へのリダイレクト return $this->redirect()->toRoute('product'); } // リクエストが POST でない場合は、正しいデータベースレコードを取得し、id と共にビューに割り当てる。 return [ 'id' => $id, 'product' => $this->table->getProduct($id), ]; } }
2-2. モデルの編集(商品データのテーブルからの削除)
products テーブルからのデータ取得、テーブルへの編集処理をする ProductTable ファイルを編集します。
編集:module/Product/src/Model/ProductTable.php
<?php namespace Product\Model; use Zend\Db\TableGateway\TableGatewayInterface; /** * 商品のデータベース・テーブルに対する操作を実行するクラス。 */ class ProductTable { // 略 /** * 渡された $id の行を完全に削除する。 * * @param string $id 商品ID * @return void */ public function deleteProduct(string $id) { $this->tableGateway->delete(['id' => (int) $id]); } }
2-3. ビューの編集(削除用のフォーム画面の作成)
作成・編集:module/Product/view/product/product/delete.phtml
<?php $title = '商品の削除'; $url = $this->url('product', ['action' => 'delete', 'id' => $id]); $this->headTitle($title); ?> <h1><?= $this->escapeHtml($title) ?></h1> <p> 「商品名:<?= $this->escapeHtml($product->itemName) ?>」を削除してよろしいですか? </p> <form action="<?= $url ?>" method="post"> <div class="form-group"> <input type="hidden" name="id" value="<?= (int) $product->id ?>" /> <input type="submit" class="btn btn-danger" name="del" value="はい" /> <input type="submit" class="btn btn-success" name="del" value="いいえ" /> </div> </form>
2-4. 機能の確認(削除してみる)
「日替わりランチB」を削除してみます。
http://localhost/product/delete/2
「はい」を押します。
※ ちなみに「いいえ」を押したら、商品一覧にリダイレクトしました。
「日替わりランチB」が削除されました!
おわりに
スペシャルランチ2000円がどんなランチか想像してお腹が空いてきたところで、今回は完了です!
次回、 ⑥ バリデーションメッセージの日本語化を投稿します。
あー!#独学エンジニア でサーバーサイドエンジニアの学習して、 #スマレジ のテックファームに転職した身としてはスクショしたくなる胸熱な広告!… pic.twitter.com/7SVrN1LeRR
— やまて|Webエンジニア2年目 (@r_yamate) 2022年4月3日
独学エンジニアで学習された方が、スマレジにいつか入社されるといいなー。