| name | code-implementer |
| description | Implements production code for PHP/CakePHP applications based on functional design specifications |
Code Implementer
A specialized skill for translating functional designs and specifications into production-ready PHP/CakePHP code.
Core Responsibilities
1. Implementation Standards
CakePHP Conventions:
// Controller naming
class UsersController extends AppController
// Model naming
class UsersTable extends Table
class User extends Entity
// Component naming
class AuthorizationComponent extends Component
// View template naming
// templates/User/Users/index.php
// templates/User/Users/view.php
2. Controller Implementation
Standard Controller Pattern:
<?php
declare(strict_types=1);
namespace App\Controller\User;
use App\Controller\AppController;
use Cake\Event\EventInterface;
use Cake\Http\Response;
/**
* Users Controller
*
* @property \App\Model\Table\UsersTable $Users
* @property \App\Controller\Component\MessageDeliveryDbAccessorComponent $MessageDeliveryDbAccessor
*/
class UsersController extends AppController
{
/**
* Initialize method
*/
public function initialize(): void
{
parent::initialize();
$this->loadComponent('MessageDeliveryDbAccessor');
}
/**
* Index method - List users
*
* @return \Cake\Http\Response|null|void
*/
public function index()
{
// Get company-specific connection
$companyId = $this->Auth->user('eco_company_id');
$conn = $this->MessageDeliveryDbAccessor
->getUserMessageDeliveryDbConnection($companyId);
$this->Users->setConnection($conn);
// Paginate with conditions
$query = $this->Users->find()
->where(['del_flg' => Configure::read('Common.del_flg.off')])
->order(['created' => 'DESC']);
$users = $this->paginate($query);
$this->set(compact('users'));
}
}
3. Model Implementation
Table Class Pattern:
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
use Cake\ORM\Query;
/**
* Users Model
*
* @property \App\Model\Table\CompanysTable&\Cake\ORM\Association\BelongsTo $Companys
* @property \App\Model\Table\OrdersTable&\Cake\ORM\Association\HasMany $Orders
*/
class UsersTable extends Table
{
/**
* Initialize method
*
* @param array $config Configuration
* @return void
*/
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('users');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->addBehavior('Timestamp');
// Associations
$this->belongsTo('Companys', [
'foreignKey' => 'company_id',
'joinType' => 'INNER',
]);
$this->hasMany('Orders', [
'foreignKey' => 'user_id',
'dependent' => true,
]);
}
/**
* Default validation rules
*
* @param \Cake\Validation\Validator $validator
* @return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator): Validator
{
$validator
->integer('id')
->allowEmptyString('id', null, 'create');
$validator
->email('email')
->requirePresence('email', 'create')
->notEmptyString('email')
->add('email', 'unique', [
'rule' => 'validateUnique',
'provider' => 'table',
'message' => __('このメールアドレスは既に登録されています'),
]);
$validator
->scalar('name')
->maxLength('name', 255)
->requirePresence('name', 'create')
->notEmptyString('name');
return $validator;
}
/**
* Build rules
*
* @param \Cake\ORM\RulesChecker $rules
* @return \Cake\ORM\RulesChecker
*/
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->isUnique(['email']));
$rules->add($rules->existsIn(['company_id'], 'Companys'));
return $rules;
}
/**
* Custom finder for active users
*
* @param \Cake\ORM\Query $query
* @param array $options
* @return \Cake\ORM\Query
*/
public function findActive(Query $query, array $options): Query
{
return $query->where([
'status' => Configure::read('User.status.active'),
'del_flg' => Configure::read('Common.del_flg.off'),
]);
}
}
Entity Class Pattern:
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
use Cake\Auth\DefaultPasswordHasher;
/**
* User Entity
*
* @property int $id
* @property string $email
* @property string $password
* @property string $name
* @property int $company_id
* @property int $status
* @property \Cake\I18n\FrozenTime $created
* @property \Cake\I18n\FrozenTime $modified
*
* @property \App\Model\Entity\Company $company
* @property \App\Model\Entity\Order[] $orders
*/
class User extends Entity
{
/**
* Accessible fields
*
* @var array
*/
protected $_accessible = [
'email' => true,
'password' => true,
'name' => true,
'company_id' => true,
'status' => true,
'created' => true,
'modified' => true,
'company' => true,
'orders' => true,
];
/**
* Hidden fields
*
* @var array
*/
protected $_hidden = [
'password',
];
/**
* Password setter with hashing
*
* @param string $password
* @return string|null
*/
protected function _setPassword(string $password): ?string
{
if (strlen($password) > 0) {
return (new DefaultPasswordHasher())->hash($password);
}
return null;
}
}
4. Component Implementation
Custom Component Pattern:
<?php
declare(strict_types=1);
namespace App\Controller\Component;
use Cake\Controller\Component;
use Cake\Core\Configure;
use Cake\Datasource\ConnectionManager;
/**
* MessageDeliveryDbAccessor component
*/
class MessageDeliveryDbAccessorComponent extends Component
{
/**
* Get company-specific database connection
*
* @param int $companyId
* @return \Cake\Database\Connection
*/
public function getUserMessageDeliveryDbConnection(int $companyId)
{
$connectionName = sprintf('Connection_[app_prefix]_company_%d', $companyId);
if (!ConnectionManager::getConfig($connectionName)) {
$config = ConnectionManager::getConfig('default');
$config['database'] = sprintf('[app_prefix]_company_%d', $companyId);
ConnectionManager::setConfig($connectionName, $config);
}
return ConnectionManager::get($connectionName);
}
}
5. View Implementation
Template Pattern:
<?php
/**
* @var \App\View\AppView $this
* @var \App\Model\Entity\User[]|\Cake\Collection\CollectionInterface $users
*/
?>
<div class="users index">
<h2><?= __('ユーザー一覧') ?></h2>
<div class="table-responsive">
<table>
<thead>
<tr>
<th><?= $this->Paginator->sort('id', 'ID') ?></th>
<th><?= $this->Paginator->sort('name', '名前') ?></th>
<th><?= $this->Paginator->sort('email', 'メール') ?></th>
<th><?= $this->Paginator->sort('created', '作成日') ?></th>
<th class="actions"><?= __('操作') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?= $this->Number->format($user->id) ?></td>
<td><?= h($user->name) ?></td>
<td><?= h($user->email) ?></td>
<td><?= h($user->created->format('Y/m/d H:i')) ?></td>
<td class="actions">
<?= $this->Html->link(__('表示'), ['action' => 'view', $user->id]) ?>
<?= $this->Html->link(__('編集'), ['action' => 'edit', $user->id]) ?>
<?= $this->Form->postLink(
__('削除'),
['action' => 'delete', $user->id],
['confirm' => __('本当に削除しますか?')]
) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="paginator">
<?= $this->Paginator->counter(__('全 {{count}} 件中 {{current}} 件を表示')) ?>
<?= $this->Paginator->first('<<') ?>
<?= $this->Paginator->prev('<') ?>
<?= $this->Paginator->numbers() ?>
<?= $this->Paginator->next('>') ?>
<?= $this->Paginator->last('>>') ?>
</div>
</div>
6. Service Layer Implementation
Business Logic Service:
<?php
declare(strict_types=1);
namespace App\Service;
use Cake\ORM\TableRegistry;
use Cake\Core\Configure;
use Cake\Log\Log;
/**
* Order Processing Service
*/
class OrderService
{
/**
* Process order
*
* @param array $orderData
* @return array Result with status and message
*/
public function processOrder(array $orderData): array
{
$Orders = TableRegistry::getTableLocator()->get('Orders');
$connection = $Orders->getConnection();
try {
$connection->begin();
// 1. Validate input
if (!$this->validateOrderData($orderData)) {
throw new \InvalidArgumentException('Invalid order data');
}
// 2. Calculate totals
$total = $this->calculateTotal($orderData['items']);
$orderData['total'] = $total;
// 3. Check inventory
if (!$this->checkInventory($orderData['items'])) {
throw new \Exception('Insufficient inventory');
}
// 4. Create order
$order = $Orders->newEntity($orderData);
if (!$Orders->save($order)) {
throw new \Exception('Failed to save order');
}
// 5. Update inventory
$this->updateInventory($orderData['items']);
// 6. Send notifications
$this->sendOrderNotification($order);
$connection->commit();
return [
'status' => 'success',
'order_id' => $order->id,
'message' => '注文が完了しました',
];
} catch (\Exception $e) {
$connection->rollback();
Log::error('Order processing failed: ' . $e->getMessage());
return [
'status' => 'error',
'message' => $e->getMessage(),
];
}
}
/**
* Calculate order total
*/
private function calculateTotal(array $items): float
{
$total = 0.0;
foreach ($items as $item) {
$total += $item['price'] * $item['quantity'];
}
return $total;
}
/**
* Check inventory availability
*/
private function checkInventory(array $items): bool
{
$Products = TableRegistry::getTableLocator()->get('Products');
foreach ($items as $item) {
$product = $Products->get($item['product_id']);
if ($product->stock < $item['quantity']) {
return false;
}
}
return true;
}
/**
* Update inventory after order
*/
private function updateInventory(array $items): void
{
$Products = TableRegistry::getTableLocator()->get('Products');
foreach ($items as $item) {
$product = $Products->get($item['product_id']);
$product->stock -= $item['quantity'];
$Products->save($product);
}
}
/**
* Send order notification
*/
private function sendOrderNotification($order): void
{
// Email notification logic
}
/**
* Validate order data
*/
private function validateOrderData(array $data): bool
{
return isset($data['user_id']) &&
isset($data['items']) &&
!empty($data['items']);
}
}
7. AJAX Handler Implementation
AJAX Response Pattern:
/**
* AJAX endpoint for data retrieval
*
* @return \Cake\Http\Response JSON response
*/
public function ajaxGetData()
{
$this->request->allowMethod(['post', 'ajax']);
$this->viewBuilder()->setOption('serialize', true);
try {
$data = $this->request->getData();
// Validate request
if (empty($data['id'])) {
throw new \InvalidArgumentException('ID is required');
}
// Get data
$result = $this->Model->find()
->where(['id' => $data['id']])
->first();
if (!$result) {
throw new \NotFoundException('Data not found');
}
$response = [
'status' => 'success',
'data' => $result->toArray(),
];
} catch (\Exception $e) {
$response = [
'status' => 'error',
'message' => $e->getMessage(),
];
}
return $this->response
->withType('application/json')
->withStringBody(json_encode($response));
}
8. Multi-Tenant Implementation
Company-Specific Data Access:
/**
* Get company-specific data
*/
public function getCompanyData()
{
// Get company ID from session
$companyId = $this->Auth->user('eco_company_id');
// Get company-specific connection
$conn = $this->MessageDeliveryDbAccessor
->getUserMessageDeliveryDbConnection($companyId);
// Switch connection for model
$this->Model->setConnection($conn);
// Query with company context
$data = $this->Model->find()
->where(['company_id' => $companyId])
->all();
return $data;
}
Implementation Patterns
1. Error Handling
try {
// Operation
$result = $this->performOperation();
if (!$result) {
throw new \RuntimeException('Operation failed');
}
} catch (\Exception $e) {
Log::error('Operation error: ' . $e->getMessage());
$this->Flash->error(__('エラーが発生しました'));
return $this->redirect(['action' => 'index']);
}
2. Transaction Management
$connection = $this->Model->getConnection();
try {
$connection->begin();
// Multiple operations
$this->Model1->save($entity1);
$this->Model2->save($entity2);
$connection->commit();
} catch (\Exception $e) {
$connection->rollback();
throw $e;
}
3. Pagination with Search
public function index()
{
$query = $this->Model->find();
// Search conditions
$search = $this->request->getQuery('search');
if (!empty($search)) {
$query->where([
'OR' => [
'name LIKE' => '%' . $search . '%',
'email LIKE' => '%' . $search . '%',
]
]);
}
$data = $this->paginate($query, [
'limit' => 20,
'order' => ['created' => 'DESC'],
]);
$this->set(compact('data', 'search'));
}
4. Authentication Check
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
// Check authentication
if (!$this->Auth->user()) {
return $this->redirect(['controller' => 'Users', 'action' => 'login']);
}
// Check authorization
$userRole = $this->Auth->user('role');
if (!in_array($userRole, ['admin', 'manager'])) {
$this->Flash->error(__('権限がありません'));
return $this->redirect('/');
}
}
Code Quality Standards
PHP Standards
// Use strict types
declare(strict_types=1);
// Type hints for parameters and returns
public function processData(array $data): bool
// Use null coalescing operator
$value = $data['key'] ?? 'default';
// Use spaceship operator for comparison
return $a <=> $b;
CakePHP Best Practices
- Use Configure::read() for configuration values
- Use TableRegistry for getting table instances
- Use ConnectionManager for database connections
- Use Log::write() for logging
- Use Flash messages for user feedback
- Use Form helper for forms
- Use Html helper for links and assets
Security Considerations
// SQL Injection Prevention - Use ORM
$users = $this->Users->find()
->where(['email' => $userInput]) // Safe
->all();
// XSS Prevention - Use h() helper
<?= h($userInput) ?> // In views
// CSRF Protection - Use Form helper
<?= $this->Form->create($entity) ?> // Automatic CSRF token
// Password Hashing - Use DefaultPasswordHasher
$hasher = new DefaultPasswordHasher();
$hashed = $hasher->hash($password);
Output Examples
Example 1: User Registration Implementation
// Controller action
public function register()
{
$user = $this->Users->newEmptyEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('登録が完了しました'));
return $this->redirect(['action' => 'login']);
}
$this->Flash->error(__('登録に失敗しました'));
}
$this->set(compact('user'));
}
Example 2: Order Processing Implementation
// Service method
public function createOrder(array $orderData): Order
{
$connection = $this->Orders->getConnection();
try {
$connection->begin();
// Create order
$order = $this->Orders->newEntity($orderData);
$this->Orders->saveOrFail($order);
// Create order items
foreach ($orderData['items'] as $itemData) {
$item = $this->OrderItems->newEntity($itemData);
$item->order_id = $order->id;
$this->OrderItems->saveOrFail($item);
}
// Update inventory
$this->InventoryService->updateStock($orderData['items']);
// Send notification
$this->NotificationService->sendOrderConfirmation($order);
$connection->commit();
return $order;
} catch (\Exception $e) {
$connection->rollback();
throw new \RuntimeException('Order creation failed: ' . $e->getMessage());
}
}
Best Practices
- Follow MVC Pattern: Keep controllers thin, models fat
- Use Services: Complex business logic in service classes
- Type Safety: Use strict types and type hints
- Error Handling: Proper try-catch blocks
- Logging: Log errors and important events
- Comments: Document complex logic
- Testing: Write testable code
Remember: Good implementation follows design specifications while maintaining code quality and framework conventions.