<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PHP  |  takeHo（たけほ）のへなちょこ台帳</title>
	<atom:link href="https://blog.takeho.com/category/web/php/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.takeho.com</link>
	<description>いわゆる自由帳ってところです。</description>
	<lastBuildDate>Tue, 14 Jul 2026 05:48:07 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6</generator>

<image>
	<url>https://blog.takeho.com/wp-content/uploads/2024/08/icon-150x150.png</url>
	<title>PHP  |  takeHo（たけほ）のへなちょこ台帳</title>
	<link>https://blog.takeho.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Yii2アプリに&#8221;会話しながらコードを書いてくれる&#8221;AIエージェントを組み込む</title>
		<link>https://blog.takeho.com/ujjf3jfa14ay1iw2kxru9dqkzv4py7jl/</link>
					<comments>https://blog.takeho.com/ujjf3jfa14ay1iw2kxru9dqkzv4py7jl/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Tue, 14 Jul 2026 10:44:00 +0000</pubDate>
				<category><![CDATA[Yii2]]></category>
		<category><![CDATA[Claude]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=1900</guid>

					<description><![CDATA[この記事で作るもの 管理画面に「AIアシスタント」タブを1つ追加し、そこにチャット欄を置くだけです。 単発の「コード生成」ではなく、要件を詰めながら会話でコードを育てていくのがポイントです。Claudeはmax_toke [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h3 class="wp-block-heading"><span id="toc1">この記事で作るもの</span></h3>



<p>管理画面に「AIアシスタント」タブを1つ追加し、そこにチャット欄を置くだけです。</p>



<pre class="wp-block-code"><code>&#91;あなた] UserモデルにSNS連携用のprovider, provider_idカラムを追加するマイグレーションを書いて
&#91;Claude] 以下のマイグレーションを作成しました。中間テーブルではなくusersテーブルへの
         直接追加案です。ユニーク制約は (provider, provider_id) の複合キーにしています…
```php
         class m260714_000000_add_sns_columns_to_users extends Migration { ... }
```
         この設計だと1ユーザーが複数SNS連携する場合に対応できません。もし複数連携を
         想定しているなら中間テーブル案も出せますが、どちらにしますか？
&#91;あなた] 複数連携できるようにして
&#91;Claude] では user_social_accounts テーブルを新設する案に切り替えます…</code></pre>



<p>単発の「コード生成」ではなく、<strong>要件を詰めながら会話でコードを育てていく</strong>のがポイントです。Claudeはmax_tokensの許す限り会話履歴を保持するので、前の提案を踏まえた修正依頼にも自然に対応できます。</p>



<h3 class="wp-block-heading"><span id="toc2">全体構成</span></h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>要素</th><th>役割</th></tr></thead><tbody><tr><td><code>components/ClaudeAgent.php</code></td><td>Claude APIを叩くYii2コンポーネント</td></tr><tr><td><code>controllers/AiAssistantController.php</code></td><td>チャットのAPIエンドポイント</td></tr><tr><td><code>models/AiConversation.php</code></td><td>会話履歴（セッション or DB保存）</td></tr><tr><td><code>views/ai-assistant/index.php</code></td><td>チャットUI（Bootstrap + Fetch API）</td></tr><tr><td><code>config/params.php</code></td><td>APIキーなどの設定</td></tr></tbody></table></figure>



<p>Yii2のDI/Componentの仕組みは、LaravelでいうServiceクラスをコンテナに登録するのとほぼ同じ感覚で扱えます。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2"><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><ol><li><a href="#toc1" tabindex="0">この記事で作るもの</a></li><li><a href="#toc2" tabindex="0">全体構成</a></li></ol></li><li><a href="#toc3" tabindex="0">1. APIキーの設定</a></li><li><a href="#toc4" tabindex="0">2. Claude APIを叩くコンポーネント</a></li><li><a href="#toc5" tabindex="0">3. 会話履歴を持つコントローラー</a></li><li><a href="#toc6" tabindex="0">4. チャットUI（View）</a></li><li><a href="#toc7" tabindex="0">5. 動作イメージ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc3">1. APIキーの設定</span></h2>



<p><code>config/params.php</code> に追記します（<code>.env</code> を使っている場合はそちら経由で読み込んでください）。</p>



<pre class="wp-block-code"><code><strong>&lt;?php</strong>
return &#91;
    'claudeApiKey' =&gt; getenv('ANTHROPIC_API_KEY'),
    'claudeModel'  =&gt; 'claude-sonnet-5', // コーディング用途はSonnetでバランス良好
];</code></pre>



<p><code>web.php</code> の <code>components</code> にも登録しておくとコントローラーから <code>Yii::$app-&gt;claudeAgent</code> で呼べて便利です。</p>



<pre class="wp-block-code"><code>'components' =&gt; &#91;
    // ...既存のコンポーネント
    'claudeAgent' =&gt; &#91;
        'class' =&gt; 'app\components\ClaudeAgent',
        'apiKey' =&gt; $params&#91;'claudeApiKey'],
        'model'  =&gt; $params&#91;'claudeModel'],
    ],
],</code></pre>



<h2 class="wp-block-heading"><span id="toc4">2. Claude APIを叩くコンポーネント</span></h2>



<p>Yii2は標準で <code>yii\httpclient\Client</code>（<code>yiisoft/yii2-httpclient</code>）が使えるので、Guzzleを別途入れなくてもOKです。未導入なら <code>composer require yiisoft/yii2-httpclient</code> を実行してください。</p>



<pre class="wp-block-code"><code><strong>&lt;?php</strong>
// components/ClaudeAgent.php

namespace app\components;

use yii\base\Component;
use yii\httpclient\Client;
use yii\base\Exception;

class ClaudeAgent extends Component
{
    public string $apiKey;
    public string $model = 'claude-sonnet-5';
    public int $maxTokens = 2048;

    private const API_URL = 'https://api.anthropic.com/v1/messages';
    private const API_VERSION = '2023-06-01';

    /**
     * @param array $history &#91;&#91;'role' =&gt; 'user'|'assistant', 'content' =&gt; string], ...]
     * @param string $systemPrompt Yii2固有の文脈を教えるシステムプロンプト
     * @return string アシスタントの返答テキスト
     */
    public function chat(array $history, string $systemPrompt): string
    {
        $client = new Client(&#91;'baseUrl' =&gt; '']);

        $response = $client-&gt;createRequest()
            -&gt;setMethod('POST')
            -&gt;setUrl(self::API_URL)
            -&gt;setHeaders(&#91;
                'x-api-key' =&gt; $this-&gt;apiKey,
                'anthropic-version' =&gt; self::API_VERSION,
                'content-type' =&gt; 'application/json',
            ])
            -&gt;setContent(json_encode(&#91;
                'model' =&gt; $this-&gt;model,
                'max_tokens' =&gt; $this-&gt;maxTokens,
                'system' =&gt; $systemPrompt,
                'messages' =&gt; $history,
            ]))
            -&gt;send();

        if (!$response-&gt;isOk) {
            throw new Exception('Claude API error: ' . $response-&gt;content);
        }

        $data = $response-&gt;data;
        // content配列からtext種別を連結（tool_use等が混じる場合を考慮）
        $text = '';
        foreach ($data&#91;'content'] as $block) {
            if ($block&#91;'type'] === 'text') {
                $text .= $block&#91;'text'];
            }
        }
        return $text;
    }
}</code></pre>



<p><strong>ポイント</strong></p>



<ul class="wp-block-list">
<li><code>system</code> パラメータにYii2固有のルール（後述）を書くことで、的外れなLaravel流の回答を防ぎます。</li>



<li><code>content</code> を配列でループしているのは、将来ツール実行（tool_use）を組み込む拡張余地を残すためです。</li>
</ul>



<h2 class="wp-block-heading"><span id="toc5">3. 会話履歴を持つコントローラー</span></h2>



<p>会話はDBに残すのが理想ですが、まずは最小構成としてセッション保存にします。</p>



<pre class="wp-block-code"><code><strong>&lt;?php</strong>
// controllers/AiAssistantController.php

namespace app\controllers;

use Yii;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\VerbFilter;

class AiAssistantController extends Controller
{
    public function behaviors()
    {
        return &#91;
            'verbs' =&gt; &#91;
                'class' =&gt; VerbFilter::class,
                'actions' =&gt; &#91;
                    'send' =&gt; &#91;'post'],
                    'reset' =&gt; &#91;'post'],
                ],
            ],
        ];
    }

    public function actionIndex()
    {
        $history = Yii::$app-&gt;session-&gt;get('ai_chat_history', &#91;]);
        return $this-&gt;render('index', &#91;'history' =&gt; $history]);
    }

    public function actionSend()
    {
        Yii::$app-&gt;response-&gt;format = Response::FORMAT_JSON;

        $message = Yii::$app-&gt;request-&gt;post('message', '');
        if (trim($message) === '') {
            return &#91;'error' =&gt; 'メッセージが空です'];
        }

        $history = Yii::$app-&gt;session-&gt;get('ai_chat_history', &#91;]);
        $history&#91;] = &#91;'role' =&gt; 'user', 'content' =&gt; $message];

        $systemPrompt = &lt;&lt;&lt;PROMPT
        あなたはYii2フレームワークの上級エンジニアです。
        - 回答はYii2の作法（ActiveRecord, Widget, Behavior, RBAC等）に従うこと
        - Laravel的な書き方（Eloquentのスコープ記法など）を混ぜないこと
        - コードは動くものを示し、設計判断が分かれる場合は選択肢を提示して確認を取ること
        - 日本語で簡潔に回答すること
        PROMPT;

        try {
            $reply = Yii::$app-&gt;claudeAgent-&gt;chat($history, $systemPrompt);
        } catch (\Throwable $e) {
            Yii::error($e-&gt;getMessage(), 'claude-agent');
            return &#91;'error' =&gt; 'AIエージェントとの通信に失敗しました'];
        }

        $history&#91;] = &#91;'role' =&gt; 'assistant', 'content' =&gt; $reply];
        Yii::$app-&gt;session-&gt;set('ai_chat_history', $history);

        return &#91;'reply' =&gt; $reply];
    }

    public function actionReset()
    {
        Yii::$app-&gt;session-&gt;remove('ai_chat_history');
        Yii::$app-&gt;response-&gt;format = Response::FORMAT_JSON;
        return &#91;'status' =&gt; 'ok'];
    }
}</code></pre>



<p><code>system</code> プロンプトで「Yii2の作法に従う」「設計判断が割れる場合は確認を取る」と明示しているのが、単なるコード生成器と対話型エージェントを分ける最大のポイントです。</p>



<h2 class="wp-block-heading"><span id="toc6">4. チャットUI（View）</span></h2>



<p>Bootstrap 5（Yii2 basic templateに標準搭載）+ 素のFetch APIだけで組めます。Reactなどは不要です。</p>



<pre class="wp-block-code"><code><strong>&lt;?php</strong>
// views/ai-assistant/index.php
/** @var array $history */
use yii\helpers\Html;
use yii\helpers\Url;

$this-&gt;title = 'AIコーディングアシスタント';
<strong>?&gt;</strong>
&lt;div class="ai-assistant-index"&gt;
    &lt;h1&gt;<strong>&lt;?=</strong> Html::encode($this-&gt;title) <strong>?&gt;</strong>&lt;/h1&gt;

    &lt;div id="chat-log" class="border rounded p-3 mb-3" style="height: 480px; overflow-y: auto; background:#fafafa;"&gt;
        <strong>&lt;?php</strong> foreach ($history as $msg): <strong>?&gt;</strong>
            &lt;div class="mb-3 &lt;?= $msg&#91;'role'] === 'user' ? 'text-end' : '' ?&gt;"&gt;
                &lt;span class="badge &lt;?= $msg&#91;'role'] === 'user' ? 'bg-primary' : 'bg-secondary' ?&gt; mb-1"&gt;
                    <strong>&lt;?=</strong> $msg&#91;'role'] === 'user' ? 'あなた' : 'Claude' <strong>?&gt;</strong>
                &lt;/span&gt;
                &lt;pre class="bg-white border rounded p-2" style="white-space: pre-wrap;"&gt;<strong>&lt;?=</strong> Html::encode($msg&#91;'content']) <strong>?&gt;</strong>&lt;/pre&gt;
            &lt;/div&gt;
        <strong>&lt;?php</strong> endforeach; <strong>?&gt;</strong>
    &lt;/div&gt;

    &lt;form id="chat-form" class="d-flex gap-2"&gt;
        &lt;input type="text" id="chat-input" class="form-control"
               placeholder="例: UserモデルにSNS連携カラムを追加したい" autocomplete="off"&gt;
        &lt;button type="submit" class="btn btn-primary"&gt;送信&lt;/button&gt;
        &lt;button type="button" id="chat-reset" class="btn btn-outline-secondary"&gt;リセット&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;

<strong>&lt;?php</strong>
$sendUrl = Url::to(&#91;'ai-assistant/send']);
$resetUrl = Url::to(&#91;'ai-assistant/reset']);
$csrfParam = Yii::$app-&gt;request-&gt;csrfParam;
$csrfToken = Yii::$app-&gt;request-&gt;csrfToken;

$js = &lt;&lt;&lt;JS
const chatLog = document.getElementById('chat-log');
const form = document.getElementById('chat-form');
const input = document.getElementById('chat-input');

function appendBubble(role, text) {
    const wrap = document.createElement('div');
    wrap.className = 'mb-3' + (role === 'user' ? ' text-end' : '');
    wrap.innerHTML = `
        &lt;span class="badge \${role === 'user' ? 'bg-primary' : 'bg-secondary'} mb-1"&gt;
            \${role === 'user' ? 'あなた' : 'Claude'}
        &lt;/span&gt;
        &lt;pre class="bg-white border rounded p-2" style="white-space: pre-wrap;"&gt;&lt;/pre&gt;
    `;
    wrap.querySelector('pre').textContent = text;
    chatLog.appendChild(wrap);
    chatLog.scrollTop = chatLog.scrollHeight;
}

form.addEventListener('submit', async (e) =&gt; {
    e.preventDefault();
    const message = input.value.trim();
    if (!message) return;

    appendBubble('user', message);
    input.value = '';

    const res = await fetch('$sendUrl', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({ message: message, '$csrfParam': '$csrfToken' }),
    });
    const data = await res.json();
    appendBubble('assistant', data.reply || ('エラー: ' + data.error));
});

document.getElementById('chat-reset').addEventListener('click', async () =&gt; {
    await fetch('$resetUrl', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({ '$csrfParam': '$csrfToken' }),
    });
    chatLog.innerHTML = '';
});
JS;
$this-&gt;registerJs($js);
<strong>?&gt;</strong></code></pre>



<h2 class="wp-block-heading"><span id="toc7">5. 動作イメージ</span></h2>



<p><code>SiteController</code> のメニューか、<code>AppAsset</code> のナビバーに以下を追加すればアクセスできます。</p>



<pre class="wp-block-code"><code>&#91;'label' =&gt; 'AIアシスタント', 'url' =&gt; &#91;'/ai-assistant/index']],</code></pre>



<p>実際にブラウザで「Userモデルに論理削除を追加して」と送ると、Claudeは</p>



<ol class="wp-block-list">
<li><code>deleted_at</code> カラム追加のマイグレーション</li>



<li><code>SoftDeleteBehavior</code>（Yii2公式の <code>yii2tech/ar-softdelete</code> or 自前Behavior）の実装案</li>



<li>既存クエリへの影響（デフォルトスコープの扱い）についての確認質問</li>
</ol>



<p>という順で、コードと一緒に「次にどうしたいか」を聞き返してくる会話になります。これがChatGPT的な単発生成との一番の違いです。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/ujjf3jfa14ay1iw2kxru9dqkzv4py7jl/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DockerでYii2開発環境を最短構築！初心者でもできるMySQL・phpMyAdmin・Mailpit連携ガイド</title>
		<link>https://blog.takeho.com/build-your-yii2-development-environment-with-docker-in-no-time-a-beginners-guide-to-integrating-mysql-phpmyadmin-and-mailpit/</link>
					<comments>https://blog.takeho.com/build-your-yii2-development-environment-with-docker-in-no-time-a-beginners-guide-to-integrating-mysql-phpmyadmin-and-mailpit/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Fri, 12 Sep 2025 11:24:00 +0000</pubDate>
				<category><![CDATA[Yii2]]></category>
		<category><![CDATA[ウェブ・開発]]></category>
		<category><![CDATA[Composer]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Mailpit]]></category>
		<category><![CDATA[MySQL]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=1275</guid>

					<description><![CDATA[本記事では、Windows環境で Yii2（Basicテンプレート） を用いたPHPアプリ開発用に、MySQL、phpMyAdmin、そしてテスト用SMTPサーバ Mailpit（メール受信UI付き）を Docker で [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>本記事では、Windows環境で <strong>Yii2（Basicテンプレート）</strong> を用いたPHPアプリ開発用に、<strong>MySQL</strong>、<strong>phpMyAdmin</strong>、そしてテスト用SMTPサーバ <strong>Mailpit</strong>（メール受信UI付き）を <strong>Docker</strong> で一気に構築する手順を解説します。プロジェクトのルートは次のパスに固定します。</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>ルート</td><td><code>C:\Work\Workspace\Docker\example.com</code></td></tr><tr><td>フレームワーク本体</td><td><code>C:\Work\Workspace\Docker\example.com\htdocs</code></td></tr><tr><td>ドキュメントルート</td><td> <code>C:\Work\Workspace\Docker\example.com\htdocs\web</code></td></tr><tr><td>Webアクセス</td><td><a href="http://localhost:8080"> http://localhost:8080</a></td></tr><tr><td>phpMyAdmin</td><td><a href="http://localhost:8081">http://localhost:8081</a></td></tr></tbody></table></figure>



<p>この記事は <strong>「そのままコピペで動く」</strong> を重視しています。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4"><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">事前準備とディレクトリ構成</a></li><li><a href="#toc2" tabindex="0">docker-compose.yml の作成</a></li><li><a href="#toc3" tabindex="0">PHP（php-fpm）用 Dockerfile と設定</a></li><li><a href="#toc4" tabindex="0">Nginx の設定</a></li><li><a href="#toc5" tabindex="0">php.ini の最低限の調整</a></li><li><a href="#toc6" tabindex="0">コンテナの起動と初期化</a></li><li><a href="#toc7" tabindex="0">Yii2 のインストール（コンテナ内でComposer実行）</a></li><li><a href="#toc8" tabindex="0">DB接続設定（.env と Yii2設定）</a></li><li><a href="#toc9" tabindex="0">Mailpit（メール送信テスト）設定</a></li><li><a href="#toc10" tabindex="0">動作確認（Web・DB・Mail）</a></li><li><a href="#toc11" tabindex="0">トラブルシューティング</a><ol><ol><li><a href="#toc12" tabindex="0">ポート競合</a></li><li><a href="#toc13" tabindex="0">MySQLへ接続できない</a></li><li><a href="#toc14" tabindex="0">Windows で権限由来の書き込みエラー</a></li><li><a href="#toc15" tabindex="0">ファイルの改行コード</a></li><li><a href="#toc16" tabindex="0">古いコンテナやイメージの削除でエラー</a></li><li><a href="#toc17" tabindex="0">Composerが遅い/タイムアウト</a></li></ol></li></ol></li><li><a href="#toc18" tabindex="0">よく使うコマンド集</a></li><li><a href="#toc19" tabindex="0">あとがき</a><ol><ol><ol><li><a href="#toc20" tabindex="0">引用元</a></li></ol></li></ol></li></ol></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">事前準備とディレクトリ構成</span></h2>



<p>以下のフォルダを作成します。</p>



<pre class="wp-block-code"><code>C:\Work\Workspace\Docker\example.com\
├─ htdocs\ ← Yii2のアプリ本体が入る（後で自動生成）
├─ docker\
│ ├─ php\
│ │ ├─ Dockerfile
│ │ └─ php.ini
│ └─ nginx\
│ └─ default.conf
└─ docker-compose.yml</code></pre>



<h2 class="wp-block-heading"><span id="toc2">docker-compose.yml の作成</span></h2>



<p><strong>ファイル:</strong> <code>C:\Work\Workspace\Docker\example.com\docker-compose.yml</code></p>



<pre class="wp-block-code"><code>services:
  nginx:
    image: nginx:1.27-alpine
    container_name: example_nginx
    ports:
      - "8080:80" # Web → http://localhost:8080
    volumes:
      - ./htdocs:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php
    networks:
      - appnet

  php:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    container_name: example_php
    volumes:
      - ./htdocs:/var/www/html
    environment:
      PHP_MEMORY_LIMIT: 512M
      PHP_MAX_EXECUTION_TIME: 120
    networks:
      - appnet
  db:
    image: mysql:8.0
    container_name: example_db
    command: &#91;"mysqld", "--default-authentication-plugin=mysql_native_password", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_0900_ai_ci"]
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: example
      MYSQL_USER: app
      MYSQL_PASSWORD: apppass
      TZ: Asia/Tokyo
    ports:
      - "3307:3306" # ローカルMySQLと衝突回避したい場合は3307→3306
    volumes:
      - dbdata:/var/lib/mysql
    networks:
      - appnet

  phpmyadmin:
    image: phpmyadmin:5-apache
    container_name: example_pma
    environment:
      PMA_HOST: db
      PMA_USER: root
      PMA_PASSWORD: root
    ports:
      - "8081:80" # phpMyAdmin → http://localhost:8081
    depends_on:
      - db
    networks:
      - appnet

  mailpit:
    image: axllent/mailpit:latest
    container_name: example_mailpit
    ports:
      - "1025:1025" # SMTP (アプリからの送信先)
      - "8025:8025" # Web UI → http://localhost:8025
    networks:
      - appnet

volumes:
  dbdata:

networks:
  appnet:
    driver: bridge</code></pre>



<div class="wp-block-cocoon-blocks-tab-caption-box-1 tab-caption-box block-box not-nested-style cocoon-block-tab-caption-box"><div class="tab-caption-box-label block-box-label box-label"><span class="tab-caption-box-label-text block-box-label-text box-label-text">補足</span></div><div class="tab-caption-box-content block-box-content box-content">
<p>既存でMySQLがWindowsに入っている場合、<code>ports</code> を <code>"3307:3306"</code> のように <strong>ホスト側を3307</strong> にすると衝突しません。</p>
</div></div>



<h2 class="wp-block-heading"><span id="toc3">PHP（php-fpm）用 Dockerfile と設定</span></h2>



<p><strong>ファイル:</strong> <code>C:\Work\Workspace\Docker\example.com\docker\php\Dockerfile</code></p>



<pre class="wp-block-code"><code>FROM php:8.3-fpm-alpine

# 必要拡張の導入
RUN apk add --no-cache \
    git zip unzip icu-dev oniguruma-dev libpng-dev libjpeg-turbo-dev libwebp-dev libzip-dev autoconf build-base \
 &amp;&amp; docker-php-ext-configure gd --with-jpeg --with-webp \
 &amp;&amp; docker-php-ext-install -j$(nproc) pdo_mysql intl mbstring gd zip exif bcmath opcache

# Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html</code></pre>



<h2 class="wp-block-heading"><span id="toc4">Nginx の設定</span></h2>



<p><strong>ファイル:</strong> <code>C:\Work\Workspace\Docker\example.com\docker\nginx\default.conf</code></p>



<pre class="wp-block-code"><code>server {
  listen 80;
  server_name example.com localhost;

  root /var/www/html/web;
  index index.php index.html;

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

  location ~ \.php$ {
    include       fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass php:9000;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
  }

  location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2?|ttf)$ {
    expires 7d;
    add_header Cache-Control "public";
  }
}</code></pre>



<h2 class="wp-block-heading"><span id="toc5">php.ini の最低限の調整</span></h2>



<p><strong>ファイル:</strong> <code>C:\Work\Workspace\Docker\example.com\docker\php\php.ini</code></p>



<pre class="wp-block-code"><code>memory_limit = ${PHP_MEMORY_LIMIT}
max_execution_time = ${PHP_MAX_EXECUTION_TIME}
upload_max_filesize = 32M
post_max_size = 32M
date.timezone = Asia/Tokyo</code></pre>



<h2 class="wp-block-heading"><span id="toc6">コンテナの起動と初期化</span></h2>



<p><strong>PowerShell</strong> で次を実行します。</p>



<pre class="wp-block-code"><code>cd C:\Work\Workspace\Docker\example.com
# 初回ビルド＆起動
docker compose up -d --build

# 状態確認
docker compose ps</code></pre>



<ul class="wp-block-list">
<li><code>http://localhost:8080</code> → Nginx（Yii2はまだ未導入なので404/エラー想定）</li>



<li><code>http://localhost:8081</code> → phpMyAdmin（<code>root</code> / <code>root</code>、サーバ: <code>db</code> でログイン可）</li>



<li><code>http://localhost:8025</code> → Mailpit（受信ボックスUI）</li>
</ul>



<h2 class="wp-block-heading"><span id="toc7">Yii2 のインストール（コンテナ内でComposer実行）</span></h2>



<p><code>htdocs</code> に Yii2 Basic を展開します。</p>



<pre class="wp-block-code"><code># PHPコンテナに入る
docker compose exec php sh

# コンテナ内で実行（/var/www/html）
composer create-project yiisoft/yii2-app-basic .
exit</code></pre>



<p>作成後、Windows側では <code>C:\Work\Workspace\Docker\example.com\htdocs</code> にファイルが展開されます。Nginx の <code>root</code> は <code>/web</code> 配下なので、<code>web/index.php</code> が入口になります。</p>



<h2 class="wp-block-heading"><span id="toc8">DB接続設定（.env と Yii2設定）</span></h2>



<p>Yii2 Basic はデフォルトで <code>.env</code> はありませんが、設定を分けたい場合は導入も可能です。ここではシンプルに <code>config/db.php</code> を直接編集します。</p>



<p><strong>ファイル:</strong> <code>htdocs\config\db.php</code></p>



<pre class="wp-block-code"><code>&lt;?php
return &#91;
  'class' => 'yii\db\Connection',
  'dsn' => 'mysql:host=db;port=3306;dbname=example',
  'username' => 'app',
  'password' => 'apppass',
  'charset' => 'utf8mb4',
  'attributes' => &#91;
    PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_STRINGIFY_FETCHES => false,
  ],
];</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><code>host</code> は <code><strong>db</strong></code>（docker-compose のサービス名）を指定します。ホストOSから直接接続する場合は <code>localhost:3307</code> です。</p>
</blockquote>



<p>初期テーブルを作る例として、マイグレーションを1本作ってみます（任意）。</p>



<pre class="wp-block-code"><code># PHPコンテナに入る
docker compose exec php sh

# マイグレーション作成
php yii migrate/create create_post_table

# 生成されたファイル(例: migrations/mYYYYMMDD_HHMMSS_create_post_table.php)を編集
exit</code></pre>



<p>サンプル（生成ファイルを編集）</p>



<pre class="wp-block-code"><code>public function safeUp()
{
  $this->createTable('{{%post}}', &#91;
    'id' => $this->primaryKey(),
    'title' => $this->string()->notNull(),
    'body' => $this->text(),
    'created_at' => $this->dateTime()->defaultExpression('CURRENT_TIMESTAMP'),
  ]);
}


public function safeDown()
{
  $this->dropTable('{{%post}}');
}</code></pre>



<pre class="wp-block-code"><code># もう一度入って実行
docker compose exec php sh
php yii migrate --interactive=0
exit</code></pre>



<h2 class="wp-block-heading"><span id="toc9">Mailpit（メール送信テスト）設定</span></h2>



<p>開発中のメールは <strong>本当に送らない</strong> のが安全です。Mailpit に流してUIで確認しましょう。</p>



<p><strong>ファイル:</strong> <code>htdocs\config\web.php</code>（<code>components</code> に <code>mailer</code> を追記）</p>



<pre class="wp-block-code"><code>'components' => &#91;
  // ... 既存設定 ...
  'mailer' => &#91;
    'class' => 'yii\swiftmailer\Mailer',
    'useFileTransport' => false, // falseでSMTPへ送信
    'transport' => &#91;
      'class' => 'Swift_SmtpTransport',
      'host' => 'mailpit',
      'port' => 1025,
      'encryption' => null,
    ],
  ],
],</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>もし Yii2 を <code>yiisoft/mailer</code> に置き換える場合は適宜設定を読み替えてください（SwiftMailerは非推奨化済みですが、Yii2 Basic既定の形のままでもMailpit連携は可能です）。</p>
</blockquote>



<p>テスト送信例（コントローラやコンソールから）</p>



<pre class="wp-block-code"><code>Yii::$app->mailer->compose()
         ->setFrom('no-reply@example.com')
         ->setTo('test@example.com')
         ->setSubject('Mailpit送信テスト')
         ->setTextBody('これはテスト本文です')
         ->send();</code></pre>



<p><code>http://localhost:8025</code> を開くと、受信メールが一覧表示されます。</p>



<h2 class="wp-block-heading"><span id="toc10">動作確認（Web・DB・Mail）</span></h2>



<ol class="wp-block-list">
<li><strong>Web</strong><br><code>http://localhost:8080</code> にアクセスして Yii2 のトップが表示されること。</li>



<li><strong>DB</strong><br><code>http://localhost:8081</code> → phpMyAdmin に <code>root/root</code> でログイン。<code>example</code> データベースに <code>post</code> テーブルができていること。</li>



<li><strong>Mail</strong><br>サンプルコードを実行し、<code>http://localhost:8025</code> の受信箱にメールが入ること。</li>
</ol>



<h2 class="wp-block-heading"><span id="toc11">トラブルシューティング</span></h2>



<h4 class="wp-block-heading"><span id="toc12">ポート競合</span></h4>



<p><code>8080</code>/<code>8081</code>/<code>8025</code>/<code>1025</code>/<code>3307</code> が使用中 → <code>docker-compose.yml</code> の <code>ports</code> 左側（ホスト側）を変更。</p>



<h4 class="wp-block-heading"><span id="toc13">MySQLへ接続できない</span></h4>



<ul class="wp-block-list">
<li><code>host</code> は <code><strong>db</strong></code>（Docker内部）か、ホスト接続では <code>localhost:3307</code> を指定。</li>



<li>初回起動直後はDBの立ち上がり待ちが必要。数秒待って再試行。</li>
</ul>



<h4 class="wp-block-heading"><span id="toc14">Windows で権限由来の書き込みエラー</span></h4>



<p><code>htdocs/runtime</code> と <code>htdocs/web/assets</code> の書き込み権限が必要。コンテナ側で <code>chmod -R 777 runtime web/assets</code>（開発用途限定）。</p>



<h4 class="wp-block-heading"><span id="toc15">ファイルの改行コード</span></h4>



<p>Nginxやシェル用ファイルは <strong>LF</strong> を推奨。VS Codeで <code>LF</code> へ変換。</p>



<h4 class="wp-block-heading"><span id="toc16">古いコンテナやイメージの削除でエラー</span></h4>



<p>例: <code>Error response from daemon: conflict: unable to delete ... image is being used by stopped container ...</code></p>



<pre class="wp-block-code"><code>docker ps -a # 停止中を含め一覧
docker rm &lt;CONTAINER_ID> # 停止中コンテナを削除
docker rmi &lt;IMAGE_ID> # 参照がなくなったらイメージ削除
# 一括停止・削除（このプロジェクト内）
docker compose down -v # コンテナ・ネットワーク・ボリューム削除
docker system prune -f # 不要なリソース掃除</code></pre>



<h4 class="wp-block-heading"><span id="toc17">Composerが遅い/タイムアウト</span></h4>



<p>一時的に <code>-vvv</code> で状況確認。社内プロキシ下では環境変数の設定を。</p>



<h2 class="wp-block-heading"><span id="toc18">よく使うコマンド集</span></h2>



<pre class="wp-block-code"><code># 起動・ビルド
cd C:\Work\Workspace\Docker\example.com
docker compose up -d --build

# 停止
docker compose down

# 停止＋ボリュームごと削除（DB初期化したいとき）
docker compose down -v

# ログ参照
docker compose logs -f nginx
docker compose logs -f php
docker compose logs -f db

# コンテナに入る
docker compose exec php sh

# 権限（開発用）
docker compose exec php sh -c "chmod -R 777 runtime web/assets"</code></pre>



<h2 class="wp-block-heading"><span id="toc19">あとがき</span></h2>



<p>ここまでで、<strong>Windows + Docker</strong> 上に <strong>Yii2 + MySQL + phpMyAdmin + Mailpit</strong> の開発環境が整いました。実運用では Mailpit の代わりに本番SMTPを使いますが、開発段階では誤送信防止のため <strong>Mailpit固定</strong> を強くおすすめします。また、MySQLのパスワードや公開ポートは本番用に必ず見直してください。必要に応じて <strong>SSL</strong> や <strong>Xdebug</strong> の導入、<strong>Nginxのキャッシュ/セキュリティヘッダ</strong> の調整などを行うと、より快適で安全な環境になります。</p>



<div class="wp-block-cocoon-blocks-blank-box-1 blank-box block-box">
<h5 class="wp-block-heading"><span id="toc20">引用元</span></h5>



<ul class="wp-block-list">
<li>Yii Framework 公式ドキュメント（Yii2 Basicテンプレート）<br><a rel="noopener" href="https://www.yiiframework.com/doc/guide/2.0/ja" target="_blank">https://www.yiiframework.com/doc/guide/2.0/ja</a></li>



<li>Docker 公式ドキュメント<br><a rel="noopener" href="https://docs.docker.com/" target="_blank">https://docs.docker.com/</a></li>



<li>Docker Compose 公式ドキュメント<br><a rel="noopener" href="https://docs.docker.com/compose/" target="_blank">https://docs.docker.com/compose/</a></li>



<li>MySQL 公式ドキュメント<br><a rel="noopener" href="https://dev.mysql.com/doc/" target="_blank">https://dev.mysql.com/doc/</a></li>



<li>phpMyAdmin 公式サイト<br><a rel="noopener" href="https://www.phpmyadmin.net/" target="_blank">https://www.phpmyadmin.net/</a></li>



<li>Mailpit（開発用SMTP &amp; Web UIツール）公式リポジトリ<br><a rel="noopener" href="https://github.com/axllent/mailpit" target="_blank">https://github.com/axllent/mailpit</a></li>



<li>Nginx 公式ドキュメント<br><a href="https://nginx.org/en/docs/">https://nginx.org/en/docs/</a></li>



<li>PHP 公式マニュアル（Docker Hub版）<br><a rel="noopener" href="https://hub.docker.com/_/php" target="_blank">https://hub.docker.com/_/php</a></li>



<li>Composer 公式サイト<br><a rel="noopener" href="https://getcomposer.org/" target="_blank">https://getcomposer.org/</a></li>
</ul>
</div>



<p></p>



<p></p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/build-your-yii2-development-environment-with-docker-in-no-time-a-beginners-guide-to-integrating-mysql-phpmyadmin-and-mailpit/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Yii3ついに登場！Yii2からどう変わった？進化点と基本構造、サンプルを一挙解説</title>
		<link>https://blog.takeho.com/yii3-is-finally-here-evolution-basic-structure-and-samples-at-a-glance/</link>
					<comments>https://blog.takeho.com/yii3-is-finally-here-evolution-basic-structure-and-samples-at-a-glance/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Fri, 04 Jul 2025 11:26:00 +0000</pubDate>
				<category><![CDATA[Yii2]]></category>
		<category><![CDATA[DI]]></category>
		<category><![CDATA[PSR]]></category>
		<category><![CDATA[Yii3]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=1206</guid>

					<description><![CDATA[PHPの人気フレームワーク「Yii2」の後継として、長らく開発が続けられていた「Yii3」がついにリリース間近となりました。公式ガイド（https://yii3.netlify.app/guide.html）も整備されつ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>PHPの人気フレームワーク「Yii2」の後継として、長らく開発が続けられていた「Yii3」がついにリリース間近となりました。<br>公式ガイド（<a class="" href="https://yii3.netlify.app/guide.html">https://yii3.netlify.app/guide.html</a>）も整備されつつあり、Yii2ユーザーやこれからPHPフレームワークを選ぼうとしている方にとって注目のタイミングです。</p>



<p>本記事では、Yii3で何が変わったのか、Yii2との違いやディレクトリ構造、実際にデータベースを使ったミニサンプルも交えて、実用的に解説します。</p>





<a rel="noopener" href="https://yii3.netlify.app/guide.html" title="https://yii3.netlify.app/guide.html" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fyii3.netlify.app%2Fguide.html?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">https://yii3.netlify.app/guide.html</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://yii3.netlify.app/guide.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">yii3.netlify.app</div></div></div></div></a>





  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6"><label class="toc-title" for="toc-checkbox-6">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">Yii2との主な違い・進化ポイント</a></li><li><a href="#toc2" tabindex="0">Yii3のディレクトリ構成</a></li><li><a href="#toc3" tabindex="0">Yii3での簡単なデータベース操作サンプル（MVC構成で作成）</a><ol><li><a href="#toc4" tabindex="0">ディレクトリ構成とファイル配置</a></li><li><a href="#toc5" tabindex="0">モデル（M） – src/Model/Post.php</a></li><li><a href="#toc6" tabindex="0">コントローラ（C） – src/Controller/SiteController.php</a></li><li><a href="#toc7" tabindex="0">ビュー（V） – src/View/site/index.php</a></li><li><a href="#toc8" tabindex="0">ルーティング設定 – config/routes.php</a></li><li><a href="#toc9" tabindex="0">アプリ設定 – config/web.php</a></li><li><a href="#toc10" tabindex="0">フロントコントローラ – public/index.php</a></li><li><a href="#toc11" tabindex="0">MySQL テーブル定義</a></li><li><a href="#toc12" tabindex="0">実行までの流れまとめ</a></li></ol></li><li><a href="#toc13" tabindex="0">補足：Yii3は「自由度の高いMVC」</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">Yii2との主な違い・進化ポイント</span></h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>項目</th><th>Yii2</th><th>Yii3</th></tr></thead><tbody><tr><td>リリース形態</td><td>モノリシック構造</td><td>パッケージ分割（コンポーザ管理）</td></tr><tr><td>DI（依存性注入）</td><td>独自コンテナ</td><td>PSR-11準拠のDIコンテナ</td></tr><tr><td>イベントシステム</td><td>グローバル依存</td><td>より明確なスコープ管理</td></tr><tr><td>HTTP処理</td><td>Yii独自実装</td><td>PSR-7/PSR-15完全対応</td></tr><tr><td>テンプレートエンジン</td><td>PHPベース（Twig等も可）</td><td>Twig対応強化、Templateパッケージ分離</td></tr><tr><td>ルーティング</td><td>独自実装</td><td>PSR準拠、Middleware対応</td></tr><tr><td>テスト性</td><td>構造がやや閉じていた</td><td>モジュール化されテストしやすく</td></tr><tr><td>コマンドライン</td><td>yiiコマンド一体型</td><td>独立したパッケージに分離（symfony/console）</td></tr></tbody></table></figure>



<p>Yii3では、近年のPHP界のトレンドであるPSR準拠を徹底しており、<strong>モダンPHPと高い互換性</strong>を持ちながら設計されています。</p>



<h2 class="wp-block-heading"><span id="toc2">Yii3のディレクトリ構成</span></h2>



<p>Yii3の標準的なアプリケーション構造は以下のようになります。</p>



<pre class="wp-block-preformatted">myapp/<br>├── config/          ← アプリ設定（DIやルーティングなど）<br>├── public/          ← Web公開ディレクトリ（index.php）<br>├── runtime/         ← ログやキャッシュなどの一時ファイル<br>├── src/             ← アプリケーションの本体コード<br>│   └── Controller/<br>│   └── Middleware/<br>│   └── View/<br>├── tests/           ← PHPUnitベースのテストコード<br>├── vendor/          ← Composer依存<br>└── composer.json</pre>



<p>「config」や「src」にアプリロジックが集約されており、明確にMVCが分離されています。</p>



<h2 class="wp-block-heading"><span id="toc3">Yii3での簡単なデータベース操作サンプル（MVC構成で作成）</span></h2>



<p>このサンプルでは、次の機能を実装します。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>「<code>post</code> テーブルの全件を取得して、一覧表示するWebページ」</p>
</blockquote>



<h3 class="wp-block-heading"><span id="toc4">ディレクトリ構成とファイル配置</span></h3>



<p>まず、以下のようにディレクトリ構成を整理します。Yii3では、<code>src/</code> がアプリケーションコードの中核です。</p>



<pre class="wp-block-code"><code>myapp/
├── config/
│   └── params.php
│   └── routes.php         ← ルーティング設定
│   └── web.php            ← DI・アプリ設定
├── src/
│   ├── Controller/
│   │   └── SiteController.php   ← コントローラ（C）
│   ├── Model/
│   │   └── Post.php             ← モデル（M）
│   ├── View/
│   │   └── site/
│   │       └── index.php        ← ビュー（V）
├── public/
│   └── index.php          ← フロントコントローラ
├── vendor/                ← Composer依存
└── composer.json</code></pre>



<h3 class="wp-block-heading"><span id="toc5">モデル（M） – src/Model/Post.php</span></h3>



<p>ActiveRecordを使用して、<code>post</code> テーブルに対応したモデルクラスを作成します。</p>



<pre class="wp-block-code"><code>&lt;?php
namespace App\Model;

use Yiisoft\ActiveRecord\ActiveRecord;

final class Post extends ActiveRecord
{
    public static function tableName(): string
    {
        return 'post';
    }
}</code></pre>



<h3 class="wp-block-heading"><span id="toc6">コントローラ（C） – src/Controller/SiteController.php</span></h3>



<p>データベースからデータを取得し、ビューに渡す役割を担います。</p>



<pre class="wp-block-code"><code>&lt;?php
namespace App\Controller;

use App\Model\Post;
use Yiisoft\Yii\View\ViewRenderer;
use Yiisoft\Router\CurrentRoute;

final class SiteController
{
    private ViewRenderer $view;

    public function __construct(ViewRenderer $view)
    {
        $this->view = $view->withControllerName('site');
    }

    public function index(): string
    {
        $posts = Post::find()->all(); // すべての投稿を取得
        return $this->view->render('index', &#91;'posts' => $posts]);
    }
}</code></pre>



<h3 class="wp-block-heading"><span id="toc7">ビュー（V） – src/View/site/index.php</span></h3>



<p>データをHTMLとして整形・表示するテンプレートファイルです。</p>



<pre class="wp-block-code"><code>&lt;h1>記事一覧&lt;/h1>

&lt;ul>
&lt;?php foreach ($posts as $post): ?>
    &lt;li>
        &lt;strong>&lt;?= htmlspecialchars($post->title) ?>&lt;/strong>&lt;br>
        &lt;?= nl2br(htmlspecialchars($post->content)) ?>
    &lt;/li>
&lt;?php endforeach; ?>
&lt;/ul></code></pre>



<h3 class="wp-block-heading"><span id="toc8">ルーティング設定 – config/routes.php</span></h3>



<p>リクエストURLとコントローラの対応関係を定義します。</p>



<pre class="wp-block-code"><code>&lt;?php

use Yiisoft\Router\Route;
use App\Controller\SiteController;

return &#91;
    Route::get('/')
        ->action(&#91;SiteController::class, 'index'])
];</code></pre>



<h3 class="wp-block-heading"><span id="toc9">アプリ設定 – config/web.php</span></h3>



<p>必要なパッケージや設定を読み込むファイルです。ここでDB接続なども設定します。</p>



<pre class="wp-block-code"><code>&lt;?php

use Yiisoft\Db\Connection\ConnectionInterface;
use Yiisoft\Db\Mysql\Connection;

return &#91;
    ConnectionInterface::class => static function () {
        return new Connection(
            dsn: 'mysql:host=localhost;dbname=yii3_demo',
            username: 'root',
            password: 'your_password',
        );
    },
];</code></pre>



<h3 class="wp-block-heading"><span id="toc10">フロントコントローラ – public/index.php</span></h3>



<p>アプリケーションのエントリーポイントです。</p>



<pre class="wp-block-code"><code>&lt;?php

declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

use Yiisoft\Yii\Web\Application;
use Yiisoft\Yii\Web\Emitter\SapiEmitter;

$container = require dirname(__DIR__) . '/config/web.php';

$app = new Application($container);

$response = $app->handle(
    $_SERVER&#91;'REQUEST_METHOD'],
    $_SERVER&#91;'REQUEST_URI']
);

(new SapiEmitter())->emit($response);</code></pre>



<h3 class="wp-block-heading"><span id="toc11">MySQL テーブル定義</span></h3>



<pre class="wp-block-code"><code>CREATE TABLE post (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL
);</code></pre>



<h3 class="wp-block-heading"><span id="toc12">実行までの流れまとめ</span></h3>



<ol class="wp-block-list">
<li>MySQLで <code>post</code> テーブルを作成する</li>



<li><code>composer install</code> で依存パッケージを取得</li>



<li><code>php -S localhost:8080 -t public</code> で開発サーバを起動</li>



<li>ブラウザで <code>http://localhost:8080</code> にアクセス</li>
</ol>



<h2 class="wp-block-heading"><span id="toc13">補足：Yii3は「自由度の高いMVC」</span></h2>



<p>Yii3は、Laravelのような「レールに乗った」MVCよりも、より<strong>柔軟で明示的なアーキテクチャ</strong>を採用しています。自分で組み立てる分、学習コストはやや高いかもしれませんが、カスタマイズ性と保守性に優れた構成です。</p>



<p>この構成に沿えば、<strong>MVCモデルの設計思想を維持しつつ、Yii3のモダンな利点</strong>を活かしたWebアプリを構築できます。<br>今後はフォームバリデーション、APIレスポンス、ログイン機能など、より実践的なトピックも記事化していきます。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/yii3-is-finally-here-evolution-basic-structure-and-samples-at-a-glance/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CodeIgniter 4のフック（Hooks）を徹底解説！</title>
		<link>https://blog.takeho.com/a-thorough-explanation-of-codeigniter-4-hooks/</link>
					<comments>https://blog.takeho.com/a-thorough-explanation-of-codeigniter-4-hooks/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Thu, 26 Jun 2025 14:41:00 +0000</pubDate>
				<category><![CDATA[CodeIgniter]]></category>
		<category><![CDATA[フック]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=1139</guid>

					<description><![CDATA[CodeIgniter 4は、軽量かつ高速なPHPフレームワークとして知られていますが、ただシンプルなだけでなく、柔軟性の高い設計が魅力のひとつです。その中でも、「フック（Hooks）」という機能は、システムの各所に任意 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>CodeIgniter 4は、軽量かつ高速なPHPフレームワークとして知られていますが、ただシンプルなだけでなく、柔軟性の高い設計が魅力のひとつです。その中でも、「フック（Hooks）」という機能は、システムの各所に任意の処理を差し込むことができる非常に強力な拡張機構です。</p>



<p>この記事では、CodeIgniter 4のHooksについて以下の観点から詳しく解説します。</p>



<ul class="wp-block-list">
<li>フックの基本概念と役割</li>



<li>使用できるイベントの一覧とタイミング</li>



<li>実用的なサンプルコード</li>



<li>サービスプロバイダを活用した高度なフック管理</li>



<li>実運用における注意点とベストプラクティス</li>
</ul>



<p>初心者の方はもちろん、すでにCodeIgniter 4を業務で利用している中級者以上の方にも、実践的な活用のヒントとなる内容を網羅的に解説します。</p>



<h3 class="wp-block-heading"><span id="toc1">フックとは何か？CodeIgniterにおける位置づけ</span></h3>



<p>フックとは、CodeIgniterのライフサイクルの特定ポイントに処理を追加できる仕組みです。たとえば、「すべてのリクエストが来る前にIP制限をかけたい」「コントローラが実行される直前にログ出力したい」といったニーズに対して、コアコードを汚さずスマートに対応できます。</p>



<p>CodeIgniter 3では、<code>application/config/hooks.php</code>に配列形式で記述するややレガシーな形式でしたが、CodeIgniter 4ではよりモダンに、イベントベースでの登録が可能になりました。これにより、PHPのオブジェクト指向的な設計とも親和性が高まり、テスト可能なコード設計も容易になります。</p>



<h3 class="wp-block-heading"><span id="toc2">利用可能なイベント一覧と活用例</span></h3>



<p>CodeIgniter 4では、以下のようなイベントが用意されており、アプリケーションの特定のタイミングで処理を挟むことが可能です。</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>イベント名</th><th>発火タイミング</th><th>主な用途・使用例</th></tr><tr><td>pre_system</td><td>システム初期化直後（LoggerやConfig未使用）</td><td>IP制限・メンテナンスモードの設定など超初期の処理</td></tr><tr><td>post_system</td><td>最終出力の直前（レスポンス確定前）</td><td>レスポンスへのヘッダー追加、ログ出力、出力内容の加工</td></tr><tr><td>pre_controller</td><td>コントローラメソッド実行直前</td><td>認証・認可処理、グローバル変数のセット、セッション検証</td></tr><tr><td>post_controller_constructor</td><td>コントローラのコンストラクタ直後</td><td>コントローラの初期化ログ出力、パラメータの加工処理</td></tr><tr><td>post_controller</td><td>コントローラ処理の終了後（レスポンス処理前）</td><td>レスポンス加工、パフォーマンス計測、例外監視処理</td></tr><tr><td>display_override</td><td>出力処理を完全に上書きする</td><td>HTML出力の置換、テンプレート全体のカスタマイズ（CLI非対応）</td></tr><tr><td>cache_override</td><td>キャッシュ機構を上書きしたい時</td><td>オリジナルのキャッシュ制御を使う必要があるとき</td></tr><tr><td>database_query</td><td>任意のSQLクエリ発行のたびに発火</td><td>クエリログ出力、SQL監視ツールとの連携、監査用途</td></tr><tr><td>email_send</td><td>Email::send() 実行後</td><td>メール送信ログ記録、外部APIへの連携通知など</td></tr><tr><td>migrations_start</td><td>マイグレーションの処理開始時</td><td>マイグレーション実行履歴の記録、メンテ通知の準備</td></tr><tr><td>migrations_end</td><td>マイグレーションの処理完了時</td><td>実行完了ログ、Slack通知などのアクション実行</td></tr><tr><td>seeds_start</td><td>シーダー処理開始</td><td>テスト環境の初期化ログ出力、外部データ取り込み準備</td></tr><tr><td>seeds_end</td><td>シーダー処理完了</td><td>シード完了通知、データ整合性チェックの実行</td></tr></tbody></table></figure>



<p>これらのイベントに対してフックを登録することで、アプリケーション全体の挙動を柔軟にカスタマイズできます。</p>



<h3 class="wp-block-heading"><span id="toc3">実践：IPアドレスによるアクセス制限のフックを実装</span></h3>



<p>以下に、特定のIP以外からのアクセスを拒否するシンプルなフックの例を紹介します。</p>



<p><code>app/Hooks/CheckIPHook.php</code></p>



<pre class="wp-block-code"><code>namespace App\Hooks;

class CheckIPHook
{
    public static function check()
    {
        $allowedIPs = &#91;'127.0.0.1', '192.168.0.1'];
        $clientIP = $_SERVER&#91;'REMOTE_ADDR'] ?? '0.0.0.0';

        if (!in_array($clientIP, $allowedIPs)) {
            http_response_code(403);
            exit('Access denied for IP: ' . $clientIP);
        }
    }
}</code></pre>



<p>このフックをイベントに登録します：</p>



<p><code>app/Config/Events.php</code></p>



<pre class="wp-block-code"><code>use App\Hooks\CheckIPHook;
use CodeIgniter\Events\Events;

Events::on('pre_system', &#91;CheckIPHook::class, 'check']);</code></pre>



<p>このように登録することで、アプリケーション全体でIP制限が適用されます。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading"><span id="toc4">実践：すべてのレスポンスにカスタムヘッダーを追加</span></h3>



<p><code>X-App-Version</code>という独自ヘッダーをレスポンスに追加する例です。</p>



<p><code>app/Hooks/AddHeaderHook.php</code></p>



<pre class="wp-block-code"><code>namespace App\Hooks;

class AddHeaderHook
{
    public static function apply()
    {
        header('X-App-Version: 1.0.0');
    }
}</code></pre>



<p><code>app/Config/Events.php</code></p>



<pre class="wp-block-code"><code>use App\Hooks\AddHeaderHook;

Events::on('post_system', &#91;AddHeaderHook::class, 'apply']);</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 class="wp-block-heading"><span id="toc5">応用：サービスプロバイダでのフック登録</span></h3>



<p>イベント登録を<code>Events.php</code>にベタ書きするのではなく、サービスプロバイダを使うことで管理しやすくなります。</p>



<p><code>app/Providers/HookEventProvider.php</code></p>



<pre class="wp-block-code"><code>namespace App\Providers;

use CodeIgniter\Events\EventsInterface;
use CodeIgniter\Service\ServiceProvider;
use App\Hooks\CheckIPHook;
use App\Hooks\AddHeaderHook;

class HookEventProvider extends ServiceProvider
{
    public function register(): void {}

    public function boot(EventsInterface $events): void
    {
        $events-&gt;on('pre_system', &#91;CheckIPHook::class, 'check']);
        $events-&gt;on('post_system', &#91;AddHeaderHook::class, 'apply']);
    }
}</code></pre>



<p>このファイルを作成後、<code>Autoload.php</code>の<code>$psr4</code>に追加すれば自動で読み込まれます。</p>



<h3 class="wp-block-heading"><span id="toc6">注意点とベストプラクティス</span></h3>



<h4 class="wp-block-heading"><span id="toc7">注意点</span></h4>



<ul class="wp-block-list">
<li><code>pre_system</code>ではLoggerやConfigが使えません。</li>



<li>header系の出力処理は<code>post_system</code>以前に行う必要があります。</li>



<li>無限ループを招くような処理やエラーハンドリングには慎重な設計が必要です。</li>
</ul>



<h4 class="wp-block-heading"><span id="toc8">ベストプラクティス</span></h4>



<ul class="wp-block-list">
<li>責任の明確な単機能クラス設計（1フック = 1責務）</li>



<li>名前空間で機能ごとに整理する</li>



<li>環境（開発／本番）に応じた動作切り替えを意識する</li>



<li>テスト用・開発用フックは明示的にON/OFF制御できるようにする</li>
</ul>



<h3 class="wp-block-heading"><span id="toc9">まとめ</span></h3>



<p>CodeIgniter 4のHooks機能は、フレームワークの処理に柔軟に介入する強力な仕組みです。Filterのようなルーティングベースの仕組みよりも、より低レベルでの制御が可能なため、セキュリティ・ロギング・バージョン管理・外部連携などさまざまなニーズに対応できます。</p>



<p>うまく使えば、処理の重複を避けてDRYなコードを実現できますが、設計や依存の整理を怠るとトラブルの原因にもなります。責任範囲を明確にしたフック設計を心がけ、CodeIgniterの柔軟なイベントシステムを最大限活用していきましょう。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/a-thorough-explanation-of-codeigniter-4-hooks/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Symfony7をさらに便利に！開発効率を爆上げする珠玉の技集</title>
		<link>https://blog.takeho.com/making-symfony-7-even-more-useful-a-collection-of-gems-to-boost-development-efficiency/</link>
					<comments>https://blog.takeho.com/making-symfony-7-even-more-useful-a-collection-of-gems-to-boost-development-efficiency/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Sun, 06 Apr 2025 04:50:07 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Cache]]></category>
		<category><![CDATA[HttpClient]]></category>
		<category><![CDATA[Messenger]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=1021</guid>

					<description><![CDATA[Symfonyは、PHPの強力なフルスタックフレームワークとして、多くの開発者に利用されています。最新バージョンのSymfony7では、さらに洗練された機能や開発体験の向上が図られています。 本記事では、Symfony7 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Symfonyは、PHPの強力なフルスタックフレームワークとして、多くの開発者に利用されています。最新バージョンのSymfony7では、さらに洗練された機能や開発体験の向上が図られています。</p>



<p>本記事では、Symfony7をより深く理解し、日々の開発業務を効率化するための便利な技を厳選してご紹介します。これらのテクニックを活用することで、コードの可読性、保守性、そして開発速度を飛躍的に向上させることができるでしょう。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-10"><label class="toc-title" for="toc-checkbox-10">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">Attribute（属性）の活用でアノテーションから脱却！</a></li><li><a href="#toc2" tabindex="0">型付きプロパティとコンストラクタープロモーションでボイラープレートを削減</a></li><li><a href="#toc3" tabindex="0">Enumerable（列挙型）をエンティティで活用する</a></li><li><a href="#toc4" tabindex="0">Messengerコンポーネントで非同期処理をスマートに</a></li><li><a href="#toc5" tabindex="0">HttpClientコンポーネントで外部API連携を簡単に</a></li><li><a href="#toc6" tabindex="0">Serializerコンポーネントでデータ変換を自由自在に</a></li><li><a href="#toc7" tabindex="0">Formコンポーネントの進化とカスタマイズ</a></li><li><a href="#toc8" tabindex="0">Securityコンポーネントの強力な認証・認可機能</a></li><li><a href="#toc9" tabindex="0">Cacheコンポーネントによるパフォーマンス向上</a></li><li><a href="#toc10" tabindex="0">Profilerとデバッグツールの活用</a></li><li><a href="#toc11" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">Attribute（属性）の活用でアノテーションから脱却！</span></h2>



<p>PHP8から導入されたAttribute（属性）は、Symfonyでも積極的に採用されています。コントローラーのルーティング定義やバリデーション、サービス定義など、これまでアノテーションで行っていた設定を、より直感的で可読性の高いAttributeで記述できます。</p>



<p><strong>例：ルーティング定義</strong></p>



<pre class="wp-block-code"><code>// アノテーションの場合 (Symfony 5/6)
/**
 * @Route("/users/{id}", name="user_show", methods={"GET"})
 */
public function show(User $user): Response
{
    // ...
}

// Attributeの場合 (Symfony 7)
#&#91;Route('/users/{id}', name: 'user_show', methods: &#91;'GET'])]
public function show(User $user): Response
{
    // ...
}</code></pre>



<p>Attributeを使用することで、クラス定義と設定がより近くに配置され、コードの意図が明確になります。</p>



<h2 class="wp-block-heading"><span id="toc2">型付きプロパティとコンストラクタープロモーションでボイラープレートを削減</span></h2>



<p>PHP7.4で導入された型付きプロパティと、PHP8.0のコンストラクタープロモーションを活用することで、クラスのプロパティ定義とコンストラクターの引数への代入処理を簡潔に記述できます。</p>



<p><strong>例：サービスオブジェクト</strong></p>



<pre class="wp-block-code"><code>// 従来の書き方
class UserService
{
    private UserRepository $userRepository;
    private LoggerInterface $logger;

    public function __construct(UserRepository $userRepository, LoggerInterface $logger)
    {
        $this-&gt;userRepository = $userRepository;
        $this-&gt;logger = $logger;
    }

    // ...
}

// コンストラクタープロモーションを使った書き方 (Symfony 7)
class UserService
{
    public function __construct(
        private UserRepository $userRepository,
        private LoggerInterface $logger
    ) {
    }

    // ...
}</code></pre>



<p>これにより、冗長なプロパティ定義と代入処理が不要になり、より重要なロジックに集中できます。</p>



<h2 class="wp-block-heading"><span id="toc3">Enumerable（列挙型）をエンティティで活用する</span></h2>



<p>PHP8.1で導入されたEnumerable（列挙型）は、エンティティの状態管理や定数管理をより安全かつ型安全に行うために役立ちます。Symfony Doctrine Bridgeとの連携もスムーズです。</p>



<p><strong>例：ユーザーのステータス管理</strong></p>



<pre class="wp-block-code"><code>// PHP 8.1 Enumerable
enum UserStatus: string
{
    case PENDING = 'pending';
    case ACTIVE = 'active';
    case INACTIVE = 'inactive';
}

// エンティティでの利用
#&#91;ORM\Entity]
class User
{
    // ...

    #&#91;ORM\Column(type: 'string', enumType: UserStatus::class)]
    private UserStatus $status = UserStatus::PENDING;

    public function getStatus(): UserStatus
    {
        return $this-&gt;status;
    }

    public function setStatus(UserStatus $status): self
    {
        $this-&gt;status = $status;
        return $this;
    }
}</code></pre>



<p>Enumerableを使用することで、マジックナンバーや文字列による比較を防ぎ、コードの可読性と保守性を向上させます。</p>



<h2 class="wp-block-heading"><span id="toc4">Messengerコンポーネントで非同期処理をスマートに</span></h2>



<p>SymfonyのMessengerコンポーネントは、メッセージキューイングシステムと連携し、時間のかかる処理をバックグラウンドで非同期的に実行するための強力なツールです。メール送信、画像処理、API連携などを非同期化することで、ユーザー体験を向上させることができます。</p>



<p><strong>基本的な流れ</strong></p>



<ol class="wp-block-list">
<li><strong>メッセージクラスの作成</strong><br>実行したい処理の内容を表すシンプルなPHPクラスを作成します。</li>



<li><strong>メッセージハンドラーの作成</strong><br>メッセージを受け取り、実際の処理を実行するクラスを作成します。</li>



<li><strong>メッセージのディスパッチ</strong><br>コントローラーやサービスからメッセージをMessengerに送信します。</li>



<li><strong>ワーカーの起動</strong><br>バックグラウンドでメッセージキューを監視し、処理を実行するワーカーを起動します。</li>
</ol>



<p>Messengerを活用することで、アプリケーションの応答性を高め、より複雑な処理を効率的に管理できます。</p>



<h2 class="wp-block-heading"><span id="toc5">HttpClientコンポーネントで外部API連携を簡単に</span></h2>



<p>SymfonyのHttpClientコンポーネントは、外部APIとの連携をシンプルかつ強力にサポートします。HTTPリクエストの送信、レスポンスの処理、認証、タイムアウト設定などを柔軟に行うことができます。</p>



<p><strong>基本的な使い方</strong></p>



<pre class="wp-block-code"><code>use Symfony\Contracts\HttpClient\HttpClientInterface;

class ApiService
{
    private HttpClientInterface $client;

    public function __construct(HttpClientInterface $client)
    {
        $this-&gt;client = $client;
    }

    public function fetchUserData(int $userId): array
    {
        $response = $this-&gt;client-&gt;request(
            'GET',
            'https://api.example.com/users/' . $userId,
            &#91;
                'headers' =&gt; &#91;
                    'Authorization' =&gt; 'Bearer YOUR_API_KEY',
                ],
            ]
        );

        return $response-&gt;toArray();
    }
}</code></pre>



<p>HttpClientを使用することで、様々な外部サービスとの連携をスムーズに行うことができます。</p>



<h2 class="wp-block-heading"><span id="toc6">Serializerコンポーネントでデータ変換を自由自在に</span></h2>



<p>SymfonyのSerializerコンポーネントは、PHPオブジェクトと様々なデータ形式（JSON、XML、YAMLなど）との相互変換を簡単に行うことができます。API開発やデータインポート/エクスポート処理などで非常に役立ちます。</p>



<p><strong>基本的な使い方</strong></p>



<pre class="wp-block-code"><code>use Symfony\Component\Serializer\SerializerInterface;

class DataConverter
{
    private SerializerInterface $serializer;

    public function __construct(SerializerInterface $serializer)
    {
        $this-&gt;serializer = $serializer;
    }

    public function serializeToJson(object $data): string
    {
        return $this-&gt;serializer-&gt;serialize($data, 'json');
    }

    public function deserializeJson(string $jsonData, string $type): object
    {
        return $this-&gt;serializer-&gt;deserialize($jsonData, $type, 'json');
    }
}</code></pre>



<p>Serializerを活用することで、データ形式に依存しない柔軟なアプリケーション開発が可能になります。</p>



<h2 class="wp-block-heading"><span id="toc7">Formコンポーネントの進化とカスタマイズ</span></h2>



<p>SymfonyのFormコンポーネントは、ユーザーからの入力を安全かつ効率的に処理するための強力なツールです。Symfony7では、より柔軟なフォームの定義やカスタマイズが可能になっています。</p>



<ul class="wp-block-list">
<li><strong>Attributeベースのフォーム定義</strong><br>フォームタイプクラス内でもAttributeを利用してフィールドを定義できます。</li>



<li><strong>型付きのフォーム</strong><br>フォームデータに型ヒントを設定することで、より安全なデータ処理を実現できます。</li>



<li><strong>イベントリスナーとデータトランスフォーマー</strong><br>複雑なフォームのロジックやデータ変換を柔軟に実装できます。</li>
</ul>



<p>Formコンポーネントを深く理解することで、複雑な入力フォームも効率的に開発できます。</p>



<h2 class="wp-block-heading"><span id="toc8">Securityコンポーネントの強力な認証・認可機能</span></h2>



<p>SymfonyのSecurityコンポーネントは、アプリケーションのセキュリティを強固にするための認証（ユーザーの識別）と認可（ユーザーのアクセス権限の管理）機能を提供します。</p>



<ul class="wp-block-list">
<li><strong>ガード認証</strong><br>より柔軟な認証フローを実装できます。</li>



<li><strong>Attributeベースのアクセス制御</strong><br>コントローラーやメソッドレベルでアクセス制御をAttributeで定義できます。</li>



<li><strong>Role階層</strong><br>複雑な権限管理を効率的に行えます。</li>
</ul>



<p>Securityコンポーネントを適切に設定することで、アプリケーションを様々な脅威から守ることができます。</p>



<h2 class="wp-block-heading"><span id="toc9">Cacheコンポーネントによるパフォーマンス向上</span></h2>



<p>SymfonyのCacheコンポーネントは、アプリケーションのパフォーマンスを向上させるための様々なキャッシュ機構を提供します。HTTPキャッシュ、アプリケーションレベルのキャッシュなど、用途に応じたキャッシュ戦略を簡単に実装できます。</p>



<ul class="wp-block-list">
<li><strong>PSR-6/PSR-16準拠</strong><br>標準化されたインターフェースで様々なキャッシュバックエンドを利用できます。</li>



<li><strong>タグ付きキャッシュ</strong><br>関連するキャッシュアイテムをまとめて削除できます。</li>



<li><strong>HTTPキャッシュの活用</strong><br>ブラウザやプロキシによるキャッシュを効率的に利用できます。</li>
</ul>



<p>Cacheコンポーネントを活用することで、アプリケーションの応答速度を大幅に改善できます。</p>



<h2 class="wp-block-heading"><span id="toc10">Profilerとデバッグツールの活用</span></h2>



<p>SymfonyのProfilerは、リクエストの処理過程で発生した様々な情報を収集し、開発者がアプリケーションの動作を詳細に分析するための強力なツールです。データベースクエリ、HTTPリクエスト、イベント、ログなどを視覚的に確認できます。</p>



<p>また、<code>dump()</code>関数や<code>dd()</code>関数などのデバッグツールを活用することで、変数の内容やプログラムの流れを簡単に確認できます。</p>



<p>これらのツールを使いこなすことで、問題の特定やパフォーマンスのボトルネックの発見が容易になります。</p>



<h2 class="wp-block-heading"><span id="toc11">まとめ</span></h2>



<p>Symfony7には、開発者の生産性を向上させ、より堅牢で保守性の高いアプリケーションを構築するための多くの便利な機能が搭載されています。今回ご紹介した技はほんの一部ですが、これらを活用することで、Symfony7での開発がより快適になるはずです。</p>



<p>ぜひ、これらのテクニックを日々の開発に取り入れて、Symfony7のパワーを最大限に引き出してください。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/making-symfony-7-even-more-useful-a-collection-of-gems-to-boost-development-efficiency/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CodeIgniter4のEntityとは？存在意義と実践的な活用法を徹底解説！</title>
		<link>https://blog.takeho.com/what-is-entity-in-ci4-a-thorough-explanation-of-its-significance-and-practical-use/</link>
					<comments>https://blog.takeho.com/what-is-entity-in-ci4-a-thorough-explanation-of-its-significance-and-practical-use/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Sat, 01 Mar 2025 16:25:00 +0000</pubDate>
				<category><![CDATA[CodeIgniter]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=720</guid>

					<description><![CDATA[目次 Entityの存在意義と役割Entityとは？なぜEntityを使うのか？Entityの使用例Entityクラスの作成Entityをモデルと組み合わせるコントローラーでの使用例データ取得時の活用Entityを使わな [&#8230;]]]></description>
										<content:encoded><![CDATA[

  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-12"><label class="toc-title" for="toc-checkbox-12">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">Entityの存在意義と役割</a><ol><li><a href="#toc2" tabindex="0">Entityとは？</a></li><li><a href="#toc3" tabindex="0">なぜEntityを使うのか？</a></li></ol></li><li><a href="#toc4" tabindex="0">Entityの使用例</a><ol><li><a href="#toc5" tabindex="0">Entityクラスの作成</a></li><li><a href="#toc6" tabindex="0">Entityをモデルと組み合わせる</a></li><li><a href="#toc7" tabindex="0">コントローラーでの使用例</a></li><li><a href="#toc8" tabindex="0">データ取得時の活用</a></li></ol></li><li><a href="#toc9" tabindex="0">Entityを使わない場合との比較</a><ol><li><a href="#toc10" tabindex="0">Entityなし（配列ベースの管理）</a></li><li><a href="#toc11" tabindex="0">Entityあり</a></li></ol></li><li><a href="#toc12" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">Entityの存在意義と役割</span></h2>



<h3 class="wp-block-heading"><span id="toc2">Entityとは？</span></h3>



<p>CodeIgniter4の<strong>Entity</strong>は、データベースから取得したデータを<strong>オブジェクト指向的に操作</strong>するための仕組みです。単なる配列やstdClassではなく、<strong>データのバリデーション・整形・ミューテーション</strong>を簡単に行えるのが特徴です。</p>



<h3 class="wp-block-heading"><span id="toc3">なぜEntityを使うのか？</span></h3>



<ul class="wp-block-list">
<li><strong>データの一貫性を保つ</strong><br>直接データを変更するのではなく、Entityを介して操作することで、バリデーションや加工処理を統一できる。</li>



<li><strong>getter/setterの活用で安全性を向上</strong><br><code>getFullName()</code> のようなカスタムメソッドを作成できるため、ロジックを一元管理しやすい。</li>



<li><strong>データのミューテーション（変換）</strong><br><code>setPassword()</code> でパスワードのハッシュ化を自動化するなど、データ変換を簡単に実装できる。</li>
</ul>



<h2 class="wp-block-heading"><span id="toc4">Entityの使用例</span></h2>



<h3 class="wp-block-heading"><span id="toc5">Entityクラスの作成</span></h3>



<p>まずは、<code>app/Entities/User.php</code> にEntityクラスを作成します。</p>



<pre class="wp-block-code"><code>namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    // 自動的にキャストするフィールド
    protected $attributes = &#91;
        'id'       => null,
        'username' => null,
        'email'    => null,
        'password' => null,
    ];

    // パスワードのハッシュ化
    public function setPassword(string $password)
    {
        $this->attributes&#91;'password'] = password_hash($password, PASSWORD_DEFAULT);
        return $this;
    }

    // フルネームを取得する例（仮）
    public function getFullName()
    {
        return $this->attributes&#91;'first_name'] . ' ' . $this->attributes&#91;'last_name'];
    }
}</code></pre>



<h3 class="wp-block-heading"><span id="toc6">Entityをモデルと組み合わせる</span></h3>



<p>次に、Entityを利用するために、<code>UserModel</code> にEntityクラスを設定します。</p>



<pre class="wp-block-code"><code>namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    protected $table = 'users';
    protected $primaryKey = 'id';
    
    protected $returnType = 'App\Entities\User'; // ここでEntityを指定する
    protected $allowedFields = &#91;'username', 'email', 'password'];

    // 自動的にタイムスタンプを更新
    protected $useTimestamps = true;
}</code></pre>



<h3 class="wp-block-heading"><span id="toc7">コントローラーでの使用例</span></h3>



<pre class="wp-block-code"><code>use App\Models\UserModel;
use App\Entities\User;

$userModel = new UserModel();

// 新しいユーザーの作成
$user = new User();
$user->username = 'testuser';
$user->email = 'test@example.com';
$user->setPassword('securepassword');

$userModel->save($user);</code></pre>



<p><code>setPassword()</code> を呼ぶことで、自動的にパスワードをハッシュ化できます。</p>



<h3 class="wp-block-heading"><span id="toc8">データ取得時の活用</span></h3>



<pre class="wp-block-code"><code>$user = $userModel->find(1);
echo $user->getFullName(); // フルネームを取得</code></pre>



<h2 class="wp-block-heading"><span id="toc9">Entityを使わない場合との比較</span></h2>



<h3 class="wp-block-heading"><span id="toc10">Entityなし（配列ベースの管理）</span></h3>



<pre class="wp-block-code"><code>$data = &#91;
    'username' => 'testuser',
    'email' => 'test@example.com',
    'password' => password_hash('securepassword', PASSWORD_DEFAULT),
];

$userModel->insert($data);</code></pre>



<p>この方法でも保存は可能ですが、<strong>ロジックをコントローラーに書いてしまうため、コードの再利用性や可読性が低下</strong>します。</p>



<h3 class="wp-block-heading"><span id="toc11">Entityあり</span></h3>



<p>Entityを使用すると、バリデーションやデータ変換を統一でき、<strong>オブジェクト指向的に管理</strong>できるため、<strong>スッキリしたコード</strong>が書けます。</p>



<h2 class="wp-block-heading"><span id="toc12">まとめ</span></h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>項目</td><td>Entityを使用しない場合</td><td>Entityを使用する場合</td></tr><tr><td>データ管理方法</td><td>配列やstdClassで管理</td><td>オブジェクトとして管理</td></tr><tr><td>データの変換</td><td>コントローラー側で行う</td><td>Entity内で統一管理</td></tr><tr><td>バリデーション</td><td>コントローラーにロジックを書く</td><td>Entity内で完結</td></tr><tr><td>可読性・再利用性</td><td>コードが散らばりやすい</td><td>一箇所に集約される</td></tr></tbody></table></figure>



<p>Entityを使うことで、データをオブジェクトとして扱い、バリデーションや整形を一元管理できるため、よりクリーンなコードを実現できます！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/what-is-entity-in-ci4-a-thorough-explanation-of-its-significance-and-practical-use/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHP5に慣れ親しんだエンジニアが掲示板をSymfony４で作成してみる</title>
		<link>https://blog.takeho.com/an-engineer-familiar-with-php5-creates-a-bulletin-board-using-symfony-4/</link>
					<comments>https://blog.takeho.com/an-engineer-familiar-with-php5-creates-a-bulletin-board-using-symfony-4/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Sun, 23 Feb 2025 01:05:00 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Composer]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[掲示板]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=666</guid>

					<description><![CDATA[Symfony4はPHP5時代と比べるとモダンな機能やベストプラクティスが多数導入されています。ここでは、ユーザ認証（ログイン・登録）、管理者権限、アクセス制限、データベースの活用といった要素を取り入れた掲示板（BBS） [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Symfony4はPHP5時代と比べるとモダンな機能やベストプラクティスが多数導入されています。ここでは、ユーザ認証（ログイン・登録）、管理者権限、アクセス制限、データベースの活用といった要素を取り入れた掲示板（BBS）を段階的に実装してSymony4を習得していきます。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-14"><label class="toc-title" for="toc-checkbox-14">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">１．必要な環境の準備</a><ol><ol><ol><li><a href="#toc2" tabindex="0">Linuxサーバの前提</a></li><li><a href="#toc3" tabindex="0">ドメイン設定</a></li></ol></li></ol></li></ol></li><li><a href="#toc4" tabindex="0">２．Apacheによるドメイン設定</a></li><li><a href="#toc5" tabindex="0">３．Symfony4プロジェクトの作成と配置</a><ol><li><a href="#toc6" tabindex="0">１．プロジェクトディレクトリの作成</a></li><li><a href="#toc7" tabindex="0">２．Symfony4プロジェクトのインストール</a></li></ol></li><li><a href="#toc8" tabindex="0">４．データベース設定とエンティティ作成</a><ol><li><a href="#toc9" tabindex="0">4.1. データベース接続情報の設定</a></li><li><a href="#toc10" tabindex="0">4.2. ユーザエンティティの作成</a></li><li><a href="#toc11" tabindex="0">4.3. 掲示板投稿（Post）エンティティの作成</a></li><li><a href="#toc12" tabindex="0">4.4. マイグレーションの実行</a></li></ol></li><li><a href="#toc13" tabindex="0">５．ユーザ認証と管理者機能の実装</a><ol><li><a href="#toc14" tabindex="0">5.1. ログイン・登録フォームの作成</a></li><li><a href="#toc15" tabindex="0">5.2. security.yaml の設定</a></li><li><a href="#toc16" tabindex="0">5.3. 管理者権限の付与</a></li></ol></li><li><a href="#toc17" tabindex="0">６．掲示板機能（BBS）の実装</a><ol><li><a href="#toc18" tabindex="0">6.1. コントローラー作成</a></li><li><a href="#toc19" tabindex="0">6.2. Twigテンプレート作成</a></li></ol></li><li><a href="#toc20" tabindex="0">７．アクセス制限とセキュリティ対策</a><ol><ol><ol><li><a href="#toc21" tabindex="0">アクセス制限</a></li><li><a href="#toc22" tabindex="0">セキュリティ対策</a></li></ol></li></ol></li></ol></li><li><a href="#toc23" tabindex="0">８．パーミッション設定と本番環境向け最適化</a><ol><li><a href="#toc24" tabindex="0">8.1. ディレクトリのパーミッション設定</a><ol><li><a href="#toc25" tabindex="0">8.2. 本番環境向け設定の調整</a></li></ol></li></ol></li><li><a href="#toc26" tabindex="0">９．テストと動作確認</a><ol><li><a href="#toc27" tabindex="0">ローカルでの動作確認</a></li><li><a href="#toc28" tabindex="0">本番サーバでの確認</a></li><li><a href="#toc29" tabindex="0">トラブルシューティング</a></li></ol></li><li><a href="#toc30" tabindex="0">１０．まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">１．必要な環境の準備</span></h2>



<h5 class="wp-block-heading"><span id="toc2">Linuxサーバの前提</span></h5>



<ul class="wp-block-list">
<li><strong>OS</strong><br>Ubuntu、CentOS、Debianなど</li>



<li><strong>PHP</strong><br>Symfony4はPHP 7.1以上が必要です<br>※ <code>php -v</code> コマンドでバージョンを確認してください</li>



<li><strong>Composer</strong><br>PHPパッケージ管理ツール</li>



<li>Webサーバ<br>ApacheまたはNginx（ここではApacheの例を示します</li>



<li></li>
</ul>



<h5 class="wp-block-heading"><span id="toc3">ドメイン設定</span></h5>



<ul class="wp-block-list">
<li>DNSで「www.example.com」が対象サーバのIPを指していることを確認してください<br></li>
</ul>



<h2 class="wp-block-heading"><span id="toc4">２．Apacheによるドメイン設定</span></h2>



<p>ApacheでのVirtualHost設定例（/etc/apache2/sites-available/example.com.conf）</p>



<pre class="wp-block-code"><code>&lt;VirtualHost *:80>
    ServerName www.example.com
    DocumentRoot /var/www/example.com/public

    &lt;Directory /var/www/example.com/public>
        AllowOverride All
        Require all granted
    &lt;/Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
&lt;/VirtualHost></code></pre>



<p>設定後、以下のコマンドで有効化と再起動を行います</p>



<pre class="wp-block-code"><code>sudo a2ensite example.com
sudo a2enmod rewrite
sudo systemctl reload apache2</code></pre>



<h2 class="wp-block-heading"><span id="toc5">３．Symfony4プロジェクトの作成と配置</span></h2>



<h3 class="wp-block-heading"><span id="toc6">１．プロジェクトディレクトリの作成</span></h3>



<p>Webサーバのドキュメントルート（例：<code>/var/www/example.com</code>）に移動し、ディレクトリを作成</p>



<pre class="wp-block-code"><code>sudo mkdir -p /var/www/example.com
cd /var/www/example.com
sudo chown $(whoami):$(whoami) .</code></pre>



<h3 class="wp-block-heading"><span id="toc7">２．Symfony4プロジェクトのインストール</span></h3>



<p>Composerを使ってSymfony4プロジェクトを作成します。今回はDoctrineやセキュリティ機能が含まれるスターターキットを利用</p>



<pre class="wp-block-code"><code>composer create-project symfony/website-skeleton board_project</code></pre>



<p>※ プロジェクトディレクトリ（例：<code>board_project</code>）内に<code>public</code>フォルダが作成されます。<br>※ ApacheのDocumentRootは、この<code>public</code>フォルダを指定してください（もしくはVirtualHost設定でパスを調整）。</p>



<h2 class="wp-block-heading"><span id="toc8">４．データベース設定とエンティティ作成</span></h2>



<p>掲示板の情報（ユーザ情報、投稿内容など）をデータベースに保存します。ここではMySQLを例にしています。</p>



<h3 class="wp-block-heading"><span id="toc9">4.1. データベース接続情報の設定</span></h3>



<p>プロジェクトルートの <code>.env</code> ファイルを編集し、<code>DATABASE_URL</code> を設定してください。</p>



<pre class="wp-block-code"><code>DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name</code></pre>



<h3 class="wp-block-heading"><span id="toc10">4.2. ユーザエンティティの作成</span></h3>



<p>Symfony MakerBundleを利用してユーザエンティティを作成します。<br></p>



<pre class="wp-block-code"><code>php bin/console make:user</code></pre>



<p>プロンプトに従い、エンティティ名（例：<code>User</code>）や識別子（メールアドレスまたはユーザ名）、パスワードフィールド、既定のロール（通常ユーザには <code>ROLE_USER</code>、管理者には <code>ROLE_ADMIN</code>）を設定します。</p>



<h3 class="wp-block-heading"><span id="toc11">4.3. 掲示板投稿（Post）エンティティの作成</span></h3>



<p>投稿情報を保存するためのエンティティを作成します。</p>



<pre class="wp-block-code"><code>php bin/console make:entity Post</code></pre>



<p>以下のフィールドを追加すると良いでしょう：</p>



<ul class="wp-block-list">
<li><code>title</code> : string</li>



<li><code>content</code> : text</li>



<li><code>createdAt</code> : datetime</li>



<li><code>user</code> : ManyToOne（Userエンティティとの関連付け、投稿者情報）</li>
</ul>



<h3 class="wp-block-heading"><span id="toc12">4.4. マイグレーションの実行</span></h3>



<p>エンティティ作成後、マイグレーションを作成してデータベースに反映させます</p>



<pre class="wp-block-code"><code>php bin/console make:migration
php bin/console doctrine:migrations:migrate</code></pre>



<h2 class="wp-block-heading"><span id="toc13">５．ユーザ認証と管理者機能の実装</span></h2>



<p>Symfonyのセキュリティコンポーネントを利用して、ログイン・登録機能、アクセス制限を実装します。</p>



<h3 class="wp-block-heading"><span id="toc14">5.1. ログイン・登録フォームの作成</span></h3>



<p>Symfony MakerBundleを使用して、ログインフォーム認証を生成します</p>



<pre class="wp-block-code"><code>php bin/console make:auth</code></pre>



<p>また、ユーザ登録フォームも作成する場合は</p>



<pre class="wp-block-code"><code>php bin/console make:registration-form</code></pre>



<p>生成されたコントローラーやフォームクラスを適宜編集し、パスワードエンコーディングやCSRF保護の設定を確認してください。</p>



<h3 class="wp-block-heading"><span id="toc15">5.2. security.yaml の設定</span></h3>



<p>プロジェクトの <code>config/packages/security.yaml</code> を以下のように設定します。必要に応じてプロバイダやファイアウォールの設定を調整してください。</p>



<pre class="wp-block-code"><code>security:
    encoders:
        App\Entity\User:
            algorithm: bcrypt

    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            lazy: true
            provider: app_user_provider
            form_login:
                login_path: login
                check_path: login
                default_target_path: /
            logout:
                path: logout

    access_control:
        # 管理者エリアへのアクセスはROLE_ADMINに制限
        - { path: ^/admin, roles: ROLE_ADMIN }
        # 投稿作成・編集はログインユーザに制限
        - { path: ^/post/new, roles: ROLE_USER }
        - { path: ^/post/edit, roles: ROLE_USER }</code></pre>



<h3 class="wp-block-heading"><span id="toc16">5.3. 管理者権限の付与</span></h3>



<p>ユーザ登録時や管理画面で、管理者ユーザには <code>ROLE_ADMIN</code> を割り当てます。管理エリア用のコントローラー（例：<code>AdminDashboardController</code>）はルートを <code>/admin</code> で始め、上記の access_control により管理者以外はアクセスできないようにします。</p>



<h2 class="wp-block-heading"><span id="toc17">６．掲示板機能（BBS）の実装</span></h2>



<p>掲示板の基本機能として、投稿の一覧表示、投稿詳細、投稿作成、編集、削除などの機能を実装します。</p>



<h3 class="wp-block-heading"><span id="toc18">6.1. コントローラー作成</span></h3>



<p>例として、<code>BoardController</code> を作成し、以下のようなアクションを実装します。</p>



<pre class="wp-block-code"><code>&lt;?php
// src/Controller/BoardController.php
namespace App\Controller;

use App\Entity\Post;
use App\Repository\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class BoardController extends AbstractController
{
    /**
     * @Route("/", name="board_index")
     */
    public function index(PostRepository $postRepository): Response
    {
        $posts = $postRepository->findBy(&#91;], &#91;'createdAt' => 'DESC']);
        return $this->render('board/index.html.twig', &#91;
            'posts' => $posts,
        ]);
    }

    /**
     * @Route("/post/{id}", name="board_view", requirements={"id"="\d+"})
     */
    public function view(Post $post): Response
    {
        return $this->render('board/view.html.twig', &#91;
            'post' => $post,
        ]);
    }

    /**
     * @Route("/post/new", name="board_new")
     */
    public function new(Request $request): Response
    {
        // ログインユーザのみ投稿可能
        $this->denyAccessUnlessGranted('ROLE_USER');

        $post = new Post();
        // フォーム作成、リクエストのハンドリング（例：Symfony Formコンポーネントを利用）
        // ※ ここでは簡易例として、フォーム処理の詳細は省略

        // 保存処理
        // $entityManager = $this->getDoctrine()->getManager();
        // $post->setCreatedAt(new \DateTime());
        // $post->setUser($this->getUser());
        // $entityManager->persist($post);
        // $entityManager->flush();

        return $this->render('board/new.html.twig', &#91;
            'post' => $post,
            // 'form' => $form->createView(),
        ]);
    }

    // 編集・削除アクションも必要に応じて実装
}</code></pre>



<h3 class="wp-block-heading"><span id="toc19">6.2. Twigテンプレート作成</span></h3>



<p><code>templates/board/</code> 配下に各ビュー用テンプレートを作成します。</p>



<p>例：<code>index.html.twig</code>（投稿一覧表示）</p>



<pre class="wp-block-code"><code>{% extends 'base.html.twig' %}

{% block title %}掲示板{% endblock %}

{% block body %}
    &lt;h1>掲示板投稿一覧&lt;/h1>
    {% for post in posts %}
        &lt;div class="post">
            &lt;h2>&lt;a href="{{ path('board_view', {id: post.id}) }}">{{ post.title }}&lt;/a>&lt;/h2>
            &lt;p>{{ post.content|raw }}&lt;/p>
            &lt;small>投稿者: {{ post.user.email }} ・ 投稿日: {{ post.createdAt|date('Y-m-d H:i') }}&lt;/small>
        &lt;/div>
        &lt;hr>
    {% else %}
        &lt;p>まだ投稿がありません。&lt;/p>
    {% endfor %}
    {% if is_granted('ROLE_USER') %}
        &lt;a href="{{ path('board_new') }}">新規投稿&lt;/a>
    {% else %}
        &lt;p>&lt;a href="{{ path('login') }}">ログイン&lt;/a>して投稿しましょう。&lt;/p>
    {% endif %}
{% endblock %}</code></pre>



<p>※ 各テンプレートは必要に応じてデザインやレイアウトを調整してください。</p>



<h2 class="wp-block-heading"><span id="toc20">７．アクセス制限とセキュリティ対策</span></h2>



<h5 class="wp-block-heading"><span id="toc21">アクセス制限</span></h5>



<ul class="wp-block-list">
<li><code>/admin</code> 以下のルートは <code>ROLE_ADMIN</code> のみアクセス可能とし、<code>security.yaml</code> の <code>access_control</code> で管理</li>



<li>投稿作成編集はログインユーザ（<code>ROLE_USER</code>）に限定するため、コントローラー内で <code>denyAccessUnlessGranted()</code> を利用</li>
</ul>



<h5 class="wp-block-heading"><span id="toc22">セキュリティ対策</span></h5>



<ul class="wp-block-list">
<li>フォームにCSRF保護を設定</li>



<li>ユーザ入力のバリデーションとサニタイズを実施</li>



<li>パスワードはエンコーダー（例：bcrypt）で暗号化</li>
</ul>



<h2 class="wp-block-heading"><span id="toc23">８．パーミッション設定と本番環境向け最適化</span></h2>



<h3 class="wp-block-heading"><span id="toc24">8.1. ディレクトリのパーミッション設定</span></h3>



<p>Symfonyはキャッシュやログを書き込むため、Apache（例：ユーザ www-data）が書き込めるようACLを設定します。</p>



<pre class="wp-block-code"><code>sudo setfacl -R -m u:www-data:rwX -m u:$(whoami):rwX var
sudo setfacl -dR -m u:www-data:rwX -m u:$(whoami):rwX var</code></pre>



<h4 class="wp-block-heading"><span id="toc25">8.2. 本番環境向け設定の調整</span></h4>



<p><code>.env</code> で環境変数を設定（例：<code>APP_ENV=prod</code>, <code>APP_DEBUG=0</code>）</p>



<p>キャッシュのウォームアップ</p>



<pre class="wp-block-code"><code>php bin/console cache:warmup --env=prod</code></pre>



<h2 class="wp-block-heading"><span id="toc26">９．テストと動作確認</span></h2>



<h3 class="wp-block-heading"><span id="toc27">ローカルでの動作確認</span></h3>



<p>ビルトインサーバを使ってローカルテスト</p>



<pre class="wp-block-code"><code>php bin/console server:run</code></pre>



<p>ブラウザで <code>http://127.0.0.1:8000</code> にアクセスし、投稿一覧やログイン・登録、投稿作成など各機能を確認してください。</p>



<h3 class="wp-block-heading"><span id="toc28">本番サーバでの確認</span></h3>



<p>DNS設定が完了していることを確認した上で、<code>http://www.example.com</code> へアクセスし、各機能が正しく動作するかテストします。</p>



<h3 class="wp-block-heading"><span id="toc29">トラブルシューティング</span></h3>



<p>エラーが発生した場合は、<code>var/log/</code> 内のログファイル（例：<code>prod.log</code> や <code>dev.log</code>）を確認し、エラーメッセージに基づいて対処してください。</p>



<h2 class="wp-block-heading"><span id="toc30">１０．まとめ</span></h2>



<p>今回のマニュアルでは、Symfony4を用いた掲示板アプリケーションの作成手順を、以下の要素を中心に解説しました</p>



<ul class="wp-block-list">
<li><strong>ユーザ認証・登録</strong><br>ログインフォームの生成とユーザエンティティ作成</li>



<li><strong>管理者権限</strong><br><code>ROLE_ADMIN</code> による管理エリアの制限</li>



<li><strong>アクセス制限</strong><br>各アクションへのアクセスをセキュリティコンポーネントで制御</li>



<li><strong>データベース連携</strong><br>Doctrineを利用したユーザ・投稿エンティティの管理</li>



<li><strong>掲示板機能</strong><br>投稿の一覧表示、詳細表示、投稿作成・編集機能の実装</li>
</ul>



<p>PHP5時代と比べ、Symfony4は構造化された設計や豊富なコンポーネントにより、拡張性・保守性に優れたアプリケーション開発が可能です。まずは本マニュアルの手順に沿って基本機能を実装し、そこからさらにカスタマイズや機能追加に挑戦してみてください。</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/an-engineer-familiar-with-php5-creates-a-bulletin-board-using-symfony-4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CodeIgniter3で複雑化するMVCを救う！HMVC導入で開発効率を大幅にアップ！！</title>
		<link>https://blog.takeho.com/the-introduction-of-hmvc-greatly-improves-development-efficiency/</link>
					<comments>https://blog.takeho.com/the-introduction-of-hmvc-greatly-improves-development-efficiency/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Wed, 12 Feb 2025 14:09:00 +0000</pubDate>
				<category><![CDATA[CodeIgniter]]></category>
		<category><![CDATA[Github]]></category>
		<category><![CDATA[HMVC]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=621</guid>

					<description><![CDATA[CodeIgniter3を使い続ける中で、複雑化するMVC構成に頭を抱えていませんか？私もその一人でした。しかし、HMVCという救世主に出会い、既存のModelを活かしながら効率的な開発環境を手に入れました。このブログで [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>CodeIgniter3を使い続ける中で、複雑化するMVC構成に頭を抱えていませんか？私もその一人でした。しかし、HMVCという救世主に出会い、既存のModelを活かしながら効率的な開発環境を手に入れました。このブログでは、その導入の背景と手順を詳しく解説します。</p>



<h2 class="wp-block-heading">背景</h2>



<p>現在、私が運用しているプロジェクトは、CodeIgniter3をベースにした中規模なウェブアプリケーションです。初期の頃はMVC構成で十分だったものの、機能追加やページ数の増加とともに、コントローラーやビューが肥大化し、保守性が低下していきました。</p>



<p>特に、ある機能を別の機能でも使い回したいとき、毎回コピペやincludeを駆使するのは面倒で非効率。そんなときに目を付けたのが「HMVCパターン」でした。</p>



<p>しかし、既存のModelはかなり作り込まれており、ゼロから作り直すのは現実的ではありません。そこで、Modelは既存のものをそのまま使用しつつ、HMVCを導入することでコントローラーとビューをモジュール化し、効率的な開発環境を目指すことにしました。</p>



<h2 class="wp-block-heading">HMVCとは？</h2>



<p>HMVC（Hierarchical Model-View-Controller）は、MVCの各要素をモジュール単位で管理するアーキテクチャです。従来のMVCでは、コントローラー、モデル、ビューがそれぞれ一つのディレクトリに配置されますが、HMVCでは「モジュール」ごとにそれらをグループ化できます。</p>



<p>例えば、<code>application/modules</code>ディレクトリ内に以下のような構造でモジュールを配置します。</p>



<pre class="wp-block-code"><code>application/modules/
  blog/
    controllers/
    views/
    models/
  shop/
    controllers/
    views/
    models/</code></pre>



<p>これにより、特定の機能を独立して管理・再利用できるようになります。</p>



<h2 class="wp-block-heading">導入手順</h2>



<h3 class="wp-block-heading"><strong>ライブラリのダウンロード</strong></h3>



<p>wiredesignzの<code>codeigniter-modular-extensions-hmvc</code>を<a href="https://github.com/wiredesignz/codeigniter-modular-extensions-hmvc">GitHub</a>からダウンロードします。</p>





<a rel="noopener" href="https://github.com/codinghamster/codeigniter-modular-extensions-hmvc" title="GitHub - codinghamster/codeigniter-modular-extensions-hmvc" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://opengraph.githubassets.com/0393c0d6010ad152e95f9166540f50fd0a83853789c62536ad54ba7e9d1722be/codinghamster/codeigniter-modular-extensions-hmvc" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">GitHub - codinghamster/codeigniter-modular-extensions-hmvc</div><div class="blogcard-snippet external-blogcard-snippet">Contribute to codinghamster/codeigniter-modular-extensions-hmvc development by creating an account on GitHub.</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/codinghamster/codeigniter-modular-extensions-hmvc" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>




<h3 class="wp-block-heading"><strong>CodeIgniterへの組み込み</strong></h3>



<p>ダウンロードしたMXディレクトリをapplication/third_partyに配置します。</p>



<p>次にcoreディレクトリにあるMY_Controller.phpとMY_Router.phpを自プロジェクトのapplication/coreにコピーします。</p>



<h3 class="wp-block-heading"><code><strong>config.php</strong></code><strong>を修正</strong></h3>



<p><strong><code>application/config/</code>config.php</strong>を次のように修正します。</p>



<pre class="wp-block-code"><code>$config&#91;'modules_locations'] = array(
    APPPATH.'modules/' => '../modules/',
);</code></pre>



<h3 class="wp-block-heading"><strong>モジュールの作成</strong></h3>



<p>例えば、ブログ機能をモジュールとして作成する場合</p>



<pre class="wp-block-code"><code><code>application/modules/blog/controllers/Blog.php</code></code></pre>



<pre class="wp-block-code"><code><code>application/modules/blog/views/index.php</code></code></pre>



<p>※Modelは既存の<code>application/models</code>内のものを使用</p>



<h3 class="wp-block-heading"><strong>モジュールの利用</strong></h3>



<p>モジュールから別のモジュールを呼び出す際もシンプルです。</p>



<pre class="wp-block-code"><code>$this->load->module('shop/cart');
$this->cart->add_item($item_id);</code></pre>



<h2 class="wp-block-heading">実装のポイント</h2>



<ul class="wp-block-list">
<li><strong>Modelは既存のものをそのまま使用</strong><br>新たにModelを作る必要がないため、導入コストを抑えられる。</li>



<li><strong>共通機能のモジュール化</strong><br>例えば、ログイン処理やフォームバリデーションなど、複数ページで使う機能をモジュール化することで、DRY（Don&#8217;t Repeat Yourself）を徹底。</li>



<li><strong>チーム開発でのメリット</strong><br>複数人で開発する場合も、モジュールごとに担当を分けることでコンフリクトを減らせる。</li>
</ul>



<h2 class="wp-block-heading">まとめ</h2>



<p>CodeIgniter3にHMVCパターンを導入することで、コードの可読性や保守性が劇的に向上しました。特に、既存のModelを活かしつつ導入できたことで、スムーズに移行できたのが大きなポイントです。</p>



<p>「CodeIgniterのMVCが限界かも…」と感じている方は、ぜひHMVCの導入を検討してみてください。</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/the-introduction-of-hmvc-greatly-improves-development-efficiency/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Yii2でメールサーバから受信ボックスのデータを取得</title>
		<link>https://blog.takeho.com/retrieve-inbox-data-from-mail-server-with-yii2/</link>
					<comments>https://blog.takeho.com/retrieve-inbox-data-from-mail-server-with-yii2/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Tue, 04 Feb 2025 14:41:00 +0000</pubDate>
				<category><![CDATA[Yii2]]></category>
		<category><![CDATA[PHP IMAP]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=608</guid>

					<description><![CDATA[Yii2でGmailやYahooメールを受信するコンポーネントを作成してみました。 目次 コンポーネントの開発ディレクトリ・ファイルの作成ファイルの作成使い方設定実行例InboxControllerViewファイルブラウ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Yii2でGmailやYahooメールを受信するコンポーネントを作成してみました。</p>



<div class="wp-block-cocoon-blocks-tab-caption-box-1 tab-caption-box block-box not-nested-style cocoon-block-tab-caption-box"><div class="tab-caption-box-label block-box-label box-label"><span class="tab-caption-box-label-text block-box-label-text box-label-text">注意事項</span></div><div class="tab-caption-box-content block-box-content box-content">
<p>PHP IMAP関数が有効である必要があります。</p>
</div></div>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-16"><label class="toc-title" for="toc-checkbox-16">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">コンポーネントの開発</a><ol><li><a href="#toc2" tabindex="0">ディレクトリ・ファイルの作成</a></li><li><a href="#toc3" tabindex="0">ファイルの作成</a></li></ol></li><li><a href="#toc4" tabindex="0">使い方</a><ol><li><a href="#toc5" tabindex="0">設定</a></li><li><a href="#toc6" tabindex="0">実行例</a><ol><li><a href="#toc7" tabindex="0">InboxController</a></li><li><a href="#toc8" tabindex="0">Viewファイル</a></li><li><a href="#toc9" tabindex="0">ブラウザで確認</a></li></ol></li></ol></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">コンポーネントの開発</span></h2>



<h3 class="wp-block-heading"><span id="toc2">ディレクトリ・ファイルの作成</span></h3>



<p>アプリケーションディレクトリ内に<strong>components</strong>ディレクトリを作成します。</p>



<p>次にそのディレクトリの中に<strong>MailFetcherComponent.php</strong>のファイルを次の内容で作成します。</p>



<h3 class="wp-block-heading"><span id="toc3">ファイルの作成</span></h3>



<pre class="wp-block-code"><code>&lt;?php

namespace app\components;
    
use Yii;
use yii\base\Component;
use yii\base\Exception;

class MailFetcherComponent extends Component
{
    // Kind ( SMTP / IMAP / POP3 ).
    public $kind;
    
    // Mail server.
    public $host;
    
    // Connect post.
    public $port;
    
    // Account user.
    public $username;
    
    // Account password.
    public $password;
    
    // Encrypt type ( PLAIN / LOGIN / CRAM-MD5 ).
    public $encryption;
    
    // Target mail folder.
    public $mailboxFolder = 'INBOX';
    
    // Seaving server directoy for attachments.
    public $attachmentPath = false;
    
    protected $inbox;
    
    public function init()
    {
        parent::init();
        
        if (!$this->host || !$this->username || !$this->password) {
            throw  new \Exception("メールサーバの情報が不足しています。");
        }
        
    }
    
    public function connectOpen($args = &#91;])
    {
        $labels = &#91; 'kind', 'host', 'port', 
                    'encryption', 'mailboxFolder'];
        
        $connectSource = "{{host}:{port}/{kind}/{encryption}}{mailboxFolder}";
        
        foreach ($labels as $label){
            if (isset($args&#91;$label])) {
                $this->$label = $args&#91;$label];
            }
            if (in_array($label, &#91;'username', 'password'])) continue;
            
            $val = $label == 'mailboxFolder' ? 
                    strtoupper($this->$label) : 
                    strtolower($this->$label); 
            
            $connectSource = str_replace('{'.$label.'}', $val, $connectSource);
        }

        $this->inbox = imap_open($connectSource, $this->username, $this->password);
        
        if (!$this->inbox) {
            echo "IMAP接続に失敗しました: " . imap_last_error();
            return;
        }
        
        if (!$this->attachmentPath) {
            $this->attachmentPath = Yii::getAlias('@runtime/mail_attachments');
        }
        
        if (!is_dir($this->attachmentPath)) {
            mkdir($this->attachmentPath, 0777, true);
        }
    }
    
    public function fetchMails($searchCriteria = 'ALL')
    {
        $sources = &#91;];
        
        $emails = imap_search($this->inbox, $searchCriteria);
        
        if ($emails) 
        {
            rsort($emails); // 最新のメールが最初に表示されるようにソート

            foreach ($emails as $emailNumber) 
            {
                $overview = imap_fetch_overview($this->inbox, $emailNumber, 0);
                
                // サブジェクトのデコード
                $subject = $this->mimeDecode($overview&#91;0]->subject);

                // 差出人のデコード
                $from = $this->mimeDecode($overview&#91;0]->from);
                
                $structure = imap_fetchstructure($this->inbox, $emailNumber);
                $message = '';
                
                
                if ($structure->type == 1) { // マルチパートメールの場合
                    
                    $attachments = array();
                    foreach ($structure->parts as $partNumber => $part) {
                        if ($part->subtype == 'PLAIN') {
                            $message = imap_fetchbody($this->inbox, $emailNumber, $partNumber + 1);

                            if ($part->encoding == 3) { // BASE64
                                $message = base64_decode($message);
                            } elseif ($part->encoding == 4) { // QUOTED-PRINTABLE
                                $message = quoted_printable_decode($message);
                            }
                            break;
                        }
                        
                        // 添付ファイルの取得
                        if (isset($part->disposition) &amp;&amp; strtolower($part->disposition) == 'attachment') {
                            $attachment = imap_fetchbody($this->inbox, $emailNumber, $partNumber + 1);

                            if ($part->encoding == 3) { // BASE64
                                $attachment = base64_decode($attachment);
                            } elseif ($part->encoding == 4) { // QUOTED-PRINTABLE
                                $attachment = quoted_printable_decode($attachment);
                            }

                            $filename = 'attachment_' . time() . '_' . ($part->dparameters&#91;0]->value ?? 'unknown');
                            file_put_contents($this->attachmentPath . '/' . $filename, $attachment);
                            $attachments&#91;] = $this->attachmentPath . '/' . $filename;
                            echo "添付ファイルを保存しました: " . $filename . "\n";
                        }
                    }
                } else { // 単一パートメールの場合
                    $message = imap_fetchbody($this->inbox, $emailNumber, 1);

                    // エンコードの処理
                    if ($structure->encoding == 3) { // BASE64
                        $message = base64_decode($message);
                    } elseif ($structure->encoding == 4) { // QUOTED-PRINTABLE
                        $message = quoted_printable_decode($message);
                    }
                }

                $sources&#91;$emailNumber] = &#91;
                    'Subject' => $subject,
                    'From' => $from,
                    'Date' => $overview&#91;0]->date,
                    'Message' => $message,
                ];
            }
        }
        
        imap_close($this->inbox);
        
        return $sources;
    }
    
    private function mimeDecode($str = '')
    {
        $encode_array = isset($str) ? imap_mime_header_decode($str) : &#91;];
        $decoded      = '';
        foreach ($encode_array as $part) {
            $decoded .= $part->text;
        }
        
        return $decoded;
    }   
}</code></pre>



<h2 class="wp-block-heading"><span id="toc4">使い方</span></h2>



<h3 class="wp-block-heading"><span id="toc5">設定</span></h3>



<p>設定ファイル（config/web.php）に作成したMailFetcherコンポーネントを次のように追加します。</p>



<pre class="wp-block-code"><code>    'components' => &#91;
        'mailFetcher' => &#91;
            'class' => 'app\components\MailFetcherComponent',
            'kind' => 'IMAP',
            'host' => 'メールサーバホスト名',  // IMAPサーバーのホスト名
            'username' => 'アカウントユーザー名',
            'password' => 'パスワード',
            'port' => 'ポート',
            'encryption' => '暗号化タイプ',
            'mailboxFolder' => 'INBOX',  // デフォルトでINBOXを使用
        ],</code></pre>



<p>上記の設定でコンポーネントをコントローラー上で実行できるようになります。</p>



<pre class="wp-block-code"><code>Yii::$app->mailFetcher;</code></pre>



<h3 class="wp-block-heading"><span id="toc6">実行例</span></h3>



<h4 class="wp-block-heading"><span id="toc7">InboxController</span></h4>



<p>InboxControllerを次のように作成します。</p>



<pre class="wp-block-code"><code>&lt;?php

namespace app\controllers;

use Yii;
use app\models\Account;

class InboxController extends \yii\web\Controller
{
    public function actionIndex()
    {
        $receiver= Yii::$app->mailFetcher;
        
        $receiver->connectOpen();
        
        return $this->render('index', &#91;
            'receiver' => $receiver->fetchMails('ALL'),
        ]);
    }
}</code></pre>



<h4 class="wp-block-heading"><span id="toc8">Viewファイル</span></h4>



<p><strong>/views/inbox/index.php</strong>を次にように編集します。</p>



<pre class="wp-block-code"><code>&lt;?php
/** @var yii\web\View $this */
?>
&lt;h1>inbox/index&lt;/h1>

&lt;p>
    You may change the content of this page by modifying
    the file &lt;code>&lt;?= __FILE__; ?>&lt;/code>.
&lt;/p>
&lt;h2>Receiver Result&lt;/h2>

&lt;?php foreach($receiver as $email): ?>

&lt;div style="padding:1em;margin-bottom:3em;border:solid 3px #999;">
    &lt;dl>
        &lt;dt>Date&lt;/dt>
        &lt;dd>&lt;?php echo $email&#91;'Date'] ?>&lt;/dd>
        &lt;dt>From&lt;/dt>
        &lt;dd>&lt;?php echo $email&#91;'From'] ?>&lt;/dd>
        &lt;dt>Subject&lt;/dt>
        &lt;dd>&lt;?php echo $email&#91;'Subject'] ?>&lt;/dd>
        &lt;dt>Message&lt;/dt>
        &lt;dd>&lt;?php echo nl2br($email&#91;'Message']) ?>&lt;/dd>
    &lt;/dl>
&lt;/div>
&lt;?php endforeach; ?></code></pre>



<h4 class="wp-block-heading"><span id="toc9">ブラウザで確認</span></h4>



<p>次のURLへアクセスして確認します。</p>



<pre class="wp-block-code"><code>http:&#47;&#47;ホスト名/index.php?r=inbox/index</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1000" height="683" src="https://blog.takeho.com/wp-content/uploads/2025/02/3483292.png" alt="" class="wp-image-615" srcset="https://blog.takeho.com/wp-content/uploads/2025/02/3483292.png 1000w, https://blog.takeho.com/wp-content/uploads/2025/02/3483292-640x437.png 640w, https://blog.takeho.com/wp-content/uploads/2025/02/3483292-768x525.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></figure>



<p>メールの受信ボックス情報が確認できれば成功です。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/retrieve-inbox-data-from-mail-server-with-yii2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CodeIgniter3でも.envが使える！煩雑な環境設定をスッキリ管理する方法</title>
		<link>https://blog.takeho.com/you-can-use-env-even-in-codeigniter3-a-clean-way-to-manage-complicated-environment-settings/</link>
					<comments>https://blog.takeho.com/you-can-use-env-even-in-codeigniter3-a-clean-way-to-manage-complicated-environment-settings/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Sun, 02 Feb 2025 09:25:25 +0000</pubDate>
				<category><![CDATA[CodeIgniter]]></category>
		<category><![CDATA[環境変数]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=625</guid>

					<description><![CDATA[Webアプリケーションを開発・運用する際、&#8221;環境ごとの設定管理&#8221;は避けて通れない課題です。特にCodeIgniter3を使っていると、configやdatabase.phpを環境ごとに書き換えたり [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Webアプリケーションを開発・運用する際、&#8221;環境ごとの設定管理&#8221;は避けて通れない課題です。特にCodeIgniter3を使っていると、<code>config</code>や<code>database.php</code>を環境ごとに書き換えたり、ファイルを分けて管理したりと、正直面倒くさいですよね。</p>



<p>実際、私が.env導入に踏み切ったのは、ローカル・ステージング・本番と複数環境を使い分けるプロジェクトで、ちょっとした設定ミスで本番DBに接続してしまい、ヒヤッとしたことがきっかけでした。</p>



<p>「これはマズイ、何とかしないと…」</p>



<p>Laravelではおなじみの<code>.env</code>。CI3でも使えたら便利だなと思い、試行錯誤の末、無事導入しました。その方法を、同じ悩みを抱える皆さんと共有したいと思います。</p>



<h2 class="wp-block-heading">.envファイルとは？</h2>



<p><strong>.env</strong>ファイルは、アプリケーションの設定値を&#8221;環境変数&#8221;として定義・管理するためのファイルです。データベース接続情報やAPIキー、アプリの動作モードなどを、この1つのファイルで切り替え可能にします。</p>



<p>CI3では標準サポートされていませんが、PHPの<strong>vlucas/phpdotenv</strong>ライブラリを活用すれば、簡単に導入できます。</p>



<h2 class="wp-block-heading">導入で何が変わるのか？</h2>



<ul class="wp-block-list">
<li><strong>設定ミスが減る</strong><br>本番・開発で設定を間違えて大惨事…を防げる！</li>



<li><strong>セキュリティ向上</strong><br>APIキーやDBパスワードをコードに直書きしない。</li>



<li><strong>メンテナンス楽々</strong><br>環境追加や設定変更が超簡単。</li>
</ul>



<h2 class="wp-block-heading">導入手順</h2>



<h3 class="wp-block-heading"><strong>.envファイルを作成</strong></h3>



<p>プロジェクトのルートに<code>.env</code>を作ります</p>



<pre class="wp-block-code"><code>CI_ENV=development
DB_HOST=localhost
DB_USER=root
DB_PASS=secret
DB_NAME=ci_project</code></pre>



<h3 class="wp-block-heading"><strong>.gitignoreに追加</strong></h3>



<pre class="wp-block-code"><code>.env</code></pre>



<h3 class="wp-block-heading"><strong>phpdotenvをインストール</strong></h3>



<pre class="wp-block-code"><code>composer require vlucas/phpdotenv</code></pre>



<h3 class="wp-block-heading"><strong>index.phpを編集</strong></h3>



<pre class="wp-block-code"><code>require __DIR__ . '/../vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
$dotenv->load();</code></pre>



<h3 class="wp-block-heading"><strong>設定ファイルでgetenv()を使う</strong></h3>



<p><code><strong>application/config/database.php</strong></code>などで</p>



<pre class="wp-block-code"><code>'hostname' => getenv('DB_HOST'),
'username' => getenv('DB_USER'),
'password' => getenv('DB_PASS'),
'database' => getenv('DB_NAME'),</code></pre>



<h2 class="wp-block-heading">導入後の変化と感想</h2>



<p>.envを導入してから、環境切り替え時のストレスが一気に減りました。何より、「本番に接続してしまった…！」という冷や汗から解放されたのは大きいです。CI3を使い続けるなら、ぜひ導入をおすすめします！</p>





<a rel="noopener" href="https://github.com/vlucas/phpdotenv" title="GitHub - vlucas/phpdotenv: Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically." class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://opengraph.githubassets.com/b5ef646ae9efea366b35d9746a808a88ae7bd70f61182d2bf7ea833c53a983cb/vlucas/phpdotenv" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">GitHub - vlucas/phpdotenv: Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.</div><div class="blogcard-snippet external-blogcard-snippet">Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically. - vlucas/phpdotenv</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://github.com/vlucas/phpdotenv" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>

]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/you-can-use-env-even-in-codeigniter3-a-clean-way-to-manage-complicated-environment-settings/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
