Claude Code Plugins

Community-maintained marketplace

Feedback

Implements production code for PHP/CakePHP applications based on functional design specifications

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

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

  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

// 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

  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.