smithery/masanao-ohba

code-implementer

Invoke when code-developer implements features in CakePHP projects. Provides CakePHP-specific patterns for controllers, models, behaviors, components, helpers, and middleware, translating functional design specs into production-ready code.

Installation

$ npx skills add smithery/masanao-ohba --skill code-implementer

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/masanao-ohba.

npx skills add smithery/masanao-ohba

Browse all from smithery/masanao-ohba

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 19,464 B
  • docs SUMMARY.md 121 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

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.