home / skills / masanao-ohba / claude-manifests / code-implementer

code-implementer skill

/skills/php-cakephp/code-implementer

This skill implements production-ready CakePHP code from functional designs across controllers, models, components, and services to accelerate delivery.

npx playbooks add skill masanao-ohba/claude-manifests --skill code-implementer

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
19.3 KB
---
name: code-implementer
description: Implements production code for PHP/CakePHP applications based on functional design specifications
hooks:
  SessionStart:
    - type: command
      command: |
        if command -v yq &> /dev/null && [ -f ".claude/config.yaml" ]; then
          echo "=== Architecture Constraints ==="
          yq -o=json '.constraints.architecture' .claude/config.yaml 2>/dev/null || true
          echo "=== Coding Standards ==="
          yq -o=json '.coding_standards' .claude/config.yaml 2>/dev/null || true
        fi
---

# Code Implementer

A specialized skill for translating functional designs and specifications into production-ready PHP/CakePHP code.

## Core Responsibilities

### 1. Implementation Standards

**CakePHP Conventions:**
```php
// 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
<?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
<?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
<?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
<?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
<?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
<?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:**
```php
/**
 * 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:**
```php
/**
 * 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
```php
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
```php
$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
```php
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
```php
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
```php
// 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
1. **Use Configure::read()** for configuration values
2. **Use TableRegistry** for getting table instances
3. **Use ConnectionManager** for database connections
4. **Use Log::write()** for logging
5. **Use Flash messages** for user feedback
6. **Use Form helper** for forms
7. **Use Html helper** for links and assets

### Security Considerations
```php
// 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
```php
// 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
```php
// 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

1. **Follow MVC Pattern**: Keep controllers thin, models fat
2. **Use Services**: Complex business logic in service classes
3. **Type Safety**: Use strict types and type hints
4. **Error Handling**: Proper try-catch blocks
5. **Logging**: Log errors and important events
6. **Comments**: Document complex logic
7. **Testing**: Write testable code

Remember: Good implementation follows design specifications while maintaining code quality and framework conventions.

Overview

This skill implements production-ready PHP and CakePHP code from functional design specifications. I translate requirements into controllers, models, entities, components, services, views, and AJAX handlers that follow CakePHP conventions and project standards. The output targets maintainability, multi-tenant support, and secure data handling.

How this skill works

I inspect functional designs and map each feature to CakePHP layers: controllers for request flow, Table and Entity classes for data modeling and validation, components for reusable utilities, services for business logic and transactions, and templates for views. I generate code that follows naming conventions, uses behaviors and associations, enforces validation and rules, and includes multi-tenant connection handling and JSON/AJAX response patterns. I also recommend transaction boundaries, error handling, and logging for robust production behavior.

When to use it

  • You have functional specs and need production-quality CakePHP implementations.
  • You need consistent controllers, models, components, and templates that follow project conventions.
  • You must add multi-tenant, company-specific database access in an existing CakePHP app.
  • You require service-layer transaction logic for complex operations like order processing.
  • You want standardized AJAX endpoints that return JSON with proper validation and error handling.

Best practices

  • Follow CakePHP naming and file layout: Controllers, Tables, Entities, Components, templates.
  • Keep business logic in service classes and use Table/Entity only for persistence concerns.
  • Wrap multi-step operations in DB transactions and log failures for traceability.
  • Use custom components for shared infrastructure (e.g., company DB accessor) and inject where needed.
  • Implement validators and buildRules for data integrity and provide clear error messages.

Example use cases

  • Implement a UsersController with company-specific DB connection and paginated index.
  • Create UsersTable and User entity with validation, unique email rule, and password hashing.
  • Build a MessageDeliveryDbAccessor component to switch connections per company ID.
  • Develop an OrderService that validates, calculates totals, checks inventory, and commits or rolls back.
  • Add an AJAX endpoint that validates input, returns JSON, and handles missing records gracefully.

FAQ

Do you enforce CakePHP naming and folder conventions?

Yes. All generated controllers, tables, entities, components, and templates follow CakePHP conventions and recommended structure.

How is multi-tenant database access handled?

I implement a component that creates or retrieves company-specific ConnectionManager configs and assigns connections to models before queries.