<?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>Symfony  |  takeHo（たけほ）のへなちょこ台帳</title>
	<atom:link href="https://blog.takeho.com/category/web/symfony/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.takeho.com</link>
	<description>いわゆる自由帳ってところです。</description>
	<lastBuildDate>Tue, 09 Dec 2025 01:24:36 +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>Symfony  |  takeHo（たけほ）のへなちょこ台帳</title>
	<link>https://blog.takeho.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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-2"><label class="toc-title" for="toc-checkbox-2">目次</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>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-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><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>Symfony4のバリテーションでユニークを設定する方法</title>
		<link>https://blog.takeho.com/post-575/</link>
					<comments>https://blog.takeho.com/post-575/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Sat, 30 Nov 2024 02:35:51 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=575</guid>

					<description><![CDATA[Symfonyは昔はYamlで様々な入力検証を設定する事ができましたが、現在はDBテーブルの定義クラスであるエンティティ（Entity）で管理・設定する事ができるようになってから各DBテーブルの定義を総括して管理できるよ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Symfonyは昔はYamlで様々な入力検証を設定する事ができましたが、現在はDBテーブルの定義クラスであるエンティティ（Entity）で管理・設定する事ができるようになってから各DBテーブルの定義を総括して管理できるようになりました。</p>



<p>このページではバリテーションで使用する可能性が高いユニーク値である事を検証する方法を説明します。</p>



<p>ここでは一般的に使用する既にログイン認証の設定が完了したユーザー（User）の定義を例にします。</p>



<p>ログイン認証までの設定方法が不明な場合は以下を参照してください。</p>



<figure class="wp-block-embed is-type-wp-embed is-provider-takeho wp-block-embed-takeho"><div class="wp-block-embed__wrapper">

<a href="https://blog.takeho.com/easily-implement-login-authentication-with-symfony4/" title="symfony4でログイン認証を簡単に実装する" 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://blog.takeho.com/wp-content/uploads/2024/07/20240717.png" 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">symfony4でログイン認証を簡単に実装する</div><div class="blogcard-snippet external-blogcard-snippet">Symfonyにはコンソールコマンドにて簡単にログイン認証を実装する事ができます。 この記事ではSymfonyのインストールが完了しているのを前提に説明しています。 前提条件 以下のcomposerコマンドにてインストールした事を前提としま</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://blog.takeho.com/pog4353byfh9f9xakd0ya815mw2w4xn8/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">blog.takeho.com</div></div></div></div></a>
</div></figure>



<p><code>/src/Entity/User.php</code>　は以下のようになっています。</p>



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

namespace App\Entity;

use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass=UserRepository::class)
 */
class User implements UserInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, unique=true)

     * @Assert\Email()
     */
    private $email;</code></pre>



<h3 class="wp-block-heading"><span id="toc1">設定方法</span></h3>



<p>メールアドレスに設定する場合、以下のように設定します。</p>



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

namespace App\Entity;

use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass=UserRepository::class)
 * @UniqueEntity("email")
 */
class User implements UserInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, unique=true)
     * @Assert\Email()
     */
    private $email;</code></pre>



<p>以上で完了です。</p>



<h3 class="wp-block-heading"><span id="toc2">クラスのアノテーションに設定する理由について</span></h3>



<p>バリテーションの設定は各カラムの変数の宣言箇所のアノテーションに記述しますが、ユニークの設定に関しては、そのテーブルの何のカラムにユニークの設定をしているのかクラスの宣言部分のアノテーションに記載する事で管理しやすくなっているものと解釈すれば納得できるかと思います。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/post-575/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PropelのページネーションとCriteriaを使ったSQLの演算子の使用例</title>
		<link>https://blog.takeho.com/4w2323lbcgziengzd7qaxvqfua9q9uup/</link>
					<comments>https://blog.takeho.com/4w2323lbcgziengzd7qaxvqfua9q9uup/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Tue, 10 Sep 2024 14:03:00 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Criteria]]></category>
		<category><![CDATA[Propel]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=352</guid>

					<description><![CDATA[PHPフレームワークのSymfonyのバージョンはこの記事を執筆した時点で7.1.4がリリースされています。composerでのパッケージ管理により便利な機能が簡単に実装できる利便性の高さは評価できますが、開発したアプリ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>PHPフレームワークのSymfonyのバージョンはこの記事を執筆した時点で7.1.4がリリースされています。composerでのパッケージ管理により便利な機能が簡単に実装できる利便性の高さは評価できますが、開発したアプリケーションに対して各パッケージの詳細まで把握する事は滅多にありませんが運用上、「把握していない」、「知らない」はあってはならない事からパッケージ管理ツールが出回る以前の古いバージョンも今なお一定の人気があります。</p>



<p>ここではSymgony1.0.17をベースにデータベースを参照したページネーション機能とデータベースへの参照問合せで頻繁に使用する演算子の使用例について紹介します。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-8"><label class="toc-title" for="toc-checkbox-8">目次</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><li><a href="#toc4" tabindex="0">ページ情報（プロパティ）の作成</a></li><li><a href="#toc5" tabindex="0">ビュー（テンプレート）の作成</a></li></ol></li><li><a href="#toc6" tabindex="0">SQLの演算子の使用例</a></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>アクションクラスへ次のメソッドを追加します。</p>



<pre class="wp-block-code"><code>  // 省略  

  public function executeList()
  {
    $c = new Criteria();
    $c->add(ProductPeer::＜カラム名＞, ＜値＞, ＜演算子(クラスメゾット)＞);
    $c->addDescendingOrderByColumn(ProductPeer::NAME);

    $pager = new sfPropelPager('Product', ＜表示させる件数＞);
    $pager->setCriteria($c);
    $pager->setPage($this->getRequestParameter('page', 0));
    $pager->init();

    $this->pager = $pager;
  }</code></pre>



<h3 class="wp-block-heading"><span id="toc3">改ページリンクの作成</span></h3>



<p>次にページャーリンクの関数を用意します。</p>



<p>アプリケーションディレクトリ内のlibディレクトリ内にhelperディレクトリを作成し次のファイルを作成します。</p>



<p>作成ファイル：<strong>PagerHelper.php</strong></p>



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

function pager_navigation($pager, $uri, $num = 8, $attach= '')
{
  $navigation = '';

  if ($pager->haveToPaginate())
  {
    $uri .= (preg_match('/\?/', $uri) ? '&amp;' : '?');
    $uri .= 'page=';

    if($num + 1 &lt; $pager->getPage()){
      $navigation .= '&lt;li>'.link_to('&lt;&lt;', $uri.'1').'&lt;/li>';
    }
    if ($pager->getPage() > 1){
      $navigation .= '&lt;li>'.link_to('&lt;', $uri.$pager->getPreviousPage()).'&lt;/li>';
    }

    $stcnt = ($pager->getPage() &lt;= $num) ? 1 : $pager->getPage() - $num;
    $encnt = ($pager->getPage() > ($pager->getLastPage() - 3)) ? $pager->getLastPage() : $pager->getPage() + $num;
    if($encnt > $pager->getLastPage()){
      $encnt = $pager->getLastPage();
    }

    for($i = $stcnt; $i &lt;= $encnt; $i++)
    {
      if ($i == $pager->getPage()) {
        // if ($page == $pager->getPage()) {
        $navigation .= '&lt;li class="active">&lt;a href="#">'.$i.'&lt;/a>&lt;/li>';
      }else {
        $navigation .= '&lt;li>'.link_to($i, $uri.$i).'&lt;/li>';
      }
    }


    if($pager->getPage() &lt;  $pager->getLastPage()){
      $navigation .= '&lt;li>'.link_to('>', $uri.$pager->getNextPage()).'&lt;/li>';
    }
    if($pager->getPage() + $num &lt;  $pager->getLastPage()){
      $navigation .= '&lt;li>'.link_to('>>', $uri.$pager->getLastPage()).'&lt;/li>';
    }
  }

  return '&lt;ul class="pagination '.$attach.'">'.$navigation.'&lt;/ul>';
}</code></pre>



<h3 class="wp-block-heading"><span id="toc4">ページ情報（プロパティ）の作成</span></h3>



<p>先程作成した<strong>PageHelper.php</strong>に表示しているページが全何件中、何処から何処まで表示しているか表示する機能を追加します。</p>



<pre class="wp-block-code"><code>function pager_infomation($pager, $format = '{total}件中 （{from}～{to}件表示）', $emp = 'Not found.')
{
  $infomation = '';
  if ($pager->getNbResults() > 0 ) {
    $total = $pager->getNbResults();
    if ($total > 0) {
      $from = $pager->getMaxPerPage() * ($pager->getPage()  - 1) + 1;
    }else {
      $from = $pager->getMaxPerPage() * ($pager->getPage()  - 1);
    }
    $to = $pager->getMaxPerPage() * ($pager->getPage()  - 1) + count($pager->getResults());
    $infomation = str_replace('{total}', $total, $format);
    $infomation = str_replace('{from}', $from, $infomation);
    $infomation = str_replace('{to}', $to, $infomation);
  }else {
    $infomation = $emp;
  }
  return $infomation;
}</code></pre>



<h3 class="wp-block-heading"><span id="toc5">ビュー（テンプレート）の作成</span></h3>



<p>次はビューファイル（listSuccess.php）の内容です。</p>



<pre class="wp-block-code"><code>&lt;?php use_helper('Pager') ?>
&lt;table>
  &lt;caption>&lt;?php echo pager_infomation($pager) ?>&lt;/caption>
  &lt;thead>
    &lt;th>品名&lt;/th>
    &lt;th>状態&lt;/th>
    &lt;th>金額&lt;/th>
    &lt;th>製造番号&lt;/th>
  &lt;/thead>
  &lt;tbody>
    &lt;?php foreach($pager->getResults() as $product): ?>
      &lt;tr>
        &lt;th>&lt;?php echo $product->getName() ?>&lt;/th>
        &lt;th>&lt;?php echo $product->getStatus() ?>&lt;/th>
        &lt;th>&lt;?php echo $product->getPrice() ?>&lt;/th>
        &lt;th>&lt;?php echo $product->getSerial() ?>&lt;/th>
      &lt;/tr>
    &lt;?php endforeach; ?>
  &lt;/tbody>
&lt;/table>
&lt;?php echo pager_navigation($pager, '＜アクションクラス名＞/list') ?></code></pre>



<p>Propelのページネーションの例は以上です。</p>



<h2 class="wp-block-heading"><span id="toc6">SQLの演算子の使用例</span></h2>



<p>次の表に演算子の使用例を紹介します。</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td class="has-text-align-left" data-align="left"><strong>SQL文</strong></td><td><strong>指定方法</strong></td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.STATUS = &#8216;enable&#8217;</td><td>$c->add(ProductPeer::STATUS, &#8216;enable&#8217;, Criteria::EQUAL);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.PRICE > 500</td><td>$c->add(ProductPeer::Price, 500, Criteria::GREATER_THAN);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.PRICE &lt; 500</td><td>$c->add(ProductPeer::Price, 500, Criteria::LESS_THAN);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.PRICE >= 500</td><td>$c->add(ProductPeer::Price, 500, Criteria::GREATER_EQUAL);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.PRICE &lt;= 500</td><td>$c->add(ProductPeer::Price, 500, Criteria::LESS_EQUAL);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.SERIAL LIKE &#8216;123%&#8217;</td><td>$c->add(ProductPeer::SERIAL, &#8216;123%&#8217;, Criteria::LIKE);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.STATUS IN(&#8216;close&#8217;, &#8216;disable&#8217;)</td><td>$c->add(ProductPeer::STATUS. array(&#8216;close&#8217;, &#8216;disable&#8217;), Criteria::IN);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.STATUS NOT IN(&#8216;close&#8217;, &#8216;disable&#8217;)</td><td>$c->add(ProductPeer::STATUS. array(&#8216;close&#8217;, &#8216;disable&#8217;), Criteria::NOT_IN);</td></tr><tr><td class="has-text-align-left" data-align="left">OFFSET 25</td><td>$c->setOffset(25);</td></tr><tr><td class="has-text-align-left" data-align="left">LIMIT 25</td><td>$c->setLimit(25);</td></tr><tr><td class="has-text-align-left" data-align="left">ORDER BY product.NAME ASC</td><td>$c->addAscendingOrderByColumn(ProductPeer::NAME);</td></tr><tr><td class="has-text-align-left" data-align="left">ORDER BY product.NAME DESC</td><td>$c->addDescendingOrderByColumn(ProductPeer::NAME);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.MESSAGE IS NULL</td><td>$c->add(ProductPeer::MESSAGE, null, Criteria::ISNULL);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.MESSAGE IS NOT NULL</td><td>$c->add(ProductPeer::MESSAGE, null, Criteria::ISNOTNULL);</td></tr><tr><td class="has-text-align-left" data-align="left">WHERE product.SERIAL REGEXP &#8216;.+456[0-9]{3}&#8217;</td><td>$c->add(ProductPeer::SERIAL, &#8221; REGEXP &#8216;.+456[0-9]{3}'&#8221;, Criteria::CUSTOM);</td></tr></tbody></table></figure>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/4w2323lbcgziengzd7qaxvqfua9q9uup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>symfony4でログイン認証を簡単に実装する</title>
		<link>https://blog.takeho.com/pog4353byfh9f9xakd0ya815mw2w4xn8/</link>
					<comments>https://blog.takeho.com/pog4353byfh9f9xakd0ya815mw2w4xn8/#respond</comments>
		
		<dc:creator><![CDATA[たけほ]]></dc:creator>
		<pubDate>Wed, 17 Jul 2024 10:12:54 +0000</pubDate>
				<category><![CDATA[Symfony]]></category>
		<guid isPermaLink="false">https://blog.takeho.com/?p=38</guid>

					<description><![CDATA[Symfonyにはコンソールコマンドにて簡単にログイン認証を実装する事ができます。 この記事ではSymfonyのインストールが完了しているのを前提に説明しています。 セキュリティーバンドルのインストール 以下のコマンドを [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Symfonyにはコンソールコマンドにて簡単にログイン認証を実装する事ができます。</p>



<p>この記事ではSymfonyのインストールが完了しているのを前提に説明しています。</p>



<div class="wp-block-cocoon-blocks-caption-box-1 caption-box block-box not-nested-style cocoon-block-caption-box"><div class="caption-box-label block-box-label box-label"><span class="caption-box-label-text block-box-label-text box-label-text">前提条件</span></div><div class="caption-box-content block-box-content box-content">
<p>以下の<strong>composer</strong>コマンドにてインストールした事を前提とします。</p>



<pre class="wp-block-code"><code>composer create-project symfony/website-skeleton &lt;PROJECT_NAME> 4.*</code></pre>
</div></div>



<h3 class="wp-block-heading"><span id="toc1">セキュリティーバンドルのインストール</span></h3>



<p>以下のコマンドを実行してセキュリティーバンドルをインストールします。</p>



<pre class="wp-block-code"><code>composer require symfony/security-bundle</code></pre>



<h3 class="wp-block-heading"><span id="toc2">ユーザークラスを作成する</span></h3>



<p>ユーザ認証に使用するためのクラスを作成します。</p>



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

The name of the security user class (e.g. User) &#91;User]:
>

Do you want to store user data in the database (via Doctrine)? (yes/no) &#91;yes]:
>

Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) &#91;email]:
>

Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).

Does this app need to hash/check user passwords? (yes/no) &#91;yes]:
>

created: src/Entity/User.php
created: src/Repository/UserRepository.php
updated: src/Entity/User.php
updated: config/packages/security.yaml


 Success!


Next Steps:
  - Review your new App\Entity\User class.
  - Use make:entity to add more fields to your User entity and then run make:migration.
  - Create a way to authenticate! See https://symfony.com/doc/current/security.html</code></pre>



<p>Userクラスのフィールドは任意で追加しても問題ありません。重要なのは<code>Symfony\Component\Security\Core\User\UserInterface</code>を継承することです。</p>



<p>クラスを作成したらデータベースを更新します。</p>



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



<h3 class="wp-block-heading"><span id="toc3">プロバイダの設定</span></h3>



<p>Userクラスに加えて、「user provider」も必要です。セッションからユーザ情報の再読み込みや、なりすまし等などいくつかのオプション機能などを支援してくれます。<br>しかし、make:userで作成すると、自動でuser providerも作成されます。security.yamlのproviders:を確認してみましょう。</p>



<pre class="wp-block-code"><code>security:
    // ...
    providers:
        # used to reload user from session &amp; other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email</code></pre>



<h3 class="wp-block-heading"><span id="toc4">パスワードエンコード</span></h3>



<p>ほとんどのログイン認証には、パスワードが使われます。パスワードは、情報漏洩対策でほとんど暗号化されています。security.yamlにパスワードの暗号方式を設定します。</p>



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



<p>次のコマンドを実行すると、パスワードを手動でエンコードできます。</p>



<pre class="wp-block-code"><code>php bin/console security:encode-password</code></pre>



<h3 class="wp-block-heading"><span id="toc5">ログインフォーム作成</span></h3>



<p>ログインフォームを作成します。</p>



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

 What style of authentication do you want? &#91;Empty authenticator]:
  &#91;0] Empty authenticator
  &#91;1] Login form authenticator
 > 1

 The class name of the authenticator to create (e.g. AppCustomAuthenticator):
 > LoginFormAuthenticator

 Choose a name for the controller class (e.g. SecurityController) &#91;SecurityController]:
 >

 Do you want to generate a '/logout' URL? (yes/no) &#91;yes]:
 >

 created: src/Security/LoginFormAuthenticator.php
 updated: config/packages/security.yaml
 created: src/Controller/SecurityController.php
 created: templates/security/login.html.twig


  Success!


 Next:
 - Customize your new authenticator.
 - Finish the redirect "TODO" in the App\Security\LoginFormAuthenticator::onAuthenticationSuccess() method.
 - Review &amp; adapt the login template: templates/security/login.html.twig.</code></pre>



<p>これにより、1）ログイン/ログアウトルートとコントローラー、2）ログインフォームをレンダリングするテンプレート、3） ログイン送信を処理するGuard認証クラス、4）メインのセキュリティ構成ファイルが更新されます。</p>



<h4 class="wp-block-heading"><span id="toc6">１．ログインコントローラー</span></h4>



<pre class="wp-block-code"><code>// src/Controller/SecurityController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class SecurityController extends AbstractController
{
    /**
     * @Route("/login", name="app_login")
     */
    public function login(AuthenticationUtils $authenticationUtils): Response
    {
        // if ($this->getUser()) {
        //     return $this->redirectToRoute('target_path');
        // }

        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render('security/login.html.twig', &#91;
            'last_username' => $lastUsername,
            'error' => $error
        ]);
    }

    /**
     * @Route("/logout", name="app_logout")
     */
    public function logout()
    {
        throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
    }
}</code></pre>



<p>ログアウトのパスを<code>security.yaml</code>に追加します。</p>



<h4 class="wp-block-heading"><span id="toc7">２．ログインフォーム</span></h4>



<p>これは、セキュリィーにはほとんど関係がなくログイン用のフォームを用意するだけです。</p>



<pre class="wp-block-code"><code>{% extends 'base.html.twig' %}
{% block title %}Log in!{% endblock %}
{% block body %}
&lt;form method="post">
{% if error %}
&lt;div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}&lt;/div>
{% endif %}
{% if app.user %}
&lt;div class="mb-3">
You are logged in as {{ app.user.username }}, &lt;a href="{{ path('app_logout') }}">Logout&lt;/a>
&lt;/div>
{% endif %}
&lt;h1 class="h3 mb-3 font-weight-normal">Please sign in&lt;/h1>
&lt;label for="inputEmail">Email&lt;/label>
&lt;input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" required autofocus>
&lt;label for="inputPassword">Password&lt;/label>
&lt;input type="password" name="password" id="inputPassword" class="form-control" required>
&lt;input type="hidden" name="_csrf_token"
value="{{ csrf_token('authenticate') }}"
>
{#
Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
See https://symfony.com/doc/current/security/remember_me.html
&lt;div class="checkbox mb-3">
&lt;label>
&lt;input type="checkbox" name="_remember_me"> Remember me
&lt;/label>
&lt;/div>
#}
&lt;button class="btn btn-lg btn-primary" type="submit">
Sign in
&lt;/button>
&lt;/form>
{% endblock %}</code></pre>



<h4 class="wp-block-heading"><span id="toc8">３．Guardオーセンティケーターがフォーム送信を処理</span></h4>



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

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = 'app_login';

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function supports(Request $request)
    {
        return self::LOGIN_ROUTE === $request->attributes->get('_route')
            &amp;&amp; $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = &#91;
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials&#91;'email']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials&#91;'csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(&#91;'email' => $credentials&#91;'email']]);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Email could not be found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials&#91;'password']);
    }

    /**
     * Used to upgrade (rehash) the user's password automatically over time.
     */
    public function getPassword($credentials): ?string
    {
        return $credentials&#91;'password'];
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

        // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
        throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }
}</code></pre>



<h4 class="wp-block-heading"><span id="toc9">４．Guardのオーセンティケーターを有効にするためにメインのセキュリティー構成ファイルを更新</span></h4>



<pre class="wp-block-code"><code>security:
    # ...

    firewalls:
        main:
            # ...
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator</code></pre>



<h3 class="wp-block-heading"><span id="toc10">アクセスコントロール</span></h3>



<p>ログインしないとシステムが使えないように、アクセス権限等を設定する必要があります。しかし、ログインはアクセスを外す必要があります。</p>



<pre class="wp-block-code"><code>security:
    # ...
    access_control:
        - { path: '^/login$', roles: IS_AUTHENTICATED_ANONYMOUSLY }
        # require ROLE_ADMIN for /admin*
        - { path: '^/', roles: ROLE_ADMIN }</code></pre>



<p>次に、ログイン成功時にTOPページに飛ぶよう設定します。ログイン認証時の処理は<code>LoginFormAuthenticator</code>を変更します。</p>



<pre class="wp-block-code"><code>// src/Security/LoginFormAuthenticator.php

// ...
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
    // ...
    // throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
    return new RedirectResponse($this->urlGenerator->generate('app_homepage'));
}</code></pre>



<h3 class="wp-block-heading"><span id="toc11">動作確認</span></h3>



<p>これで、ログイン認証処理は終わりです。では実際に動作確認をする方法を説明します。</p>



<h4 class="wp-block-heading"><span id="toc12">事前準備</span></h4>



<p>確認するには予めデータベースへアカウント情報を登録する必要があります。</p>



<p>データベースへ登録するデータはメールアドレスとパスワードの２つになりますが、パスワードは暗号化したハッシュ値を保存します。</p>



<p>暗号化したハッシュ値はパスワードエンコードの項目で説明した以下のコマンドで確認する事ができます。</p>



<pre class="wp-block-code"><code>>php bin/console security:encode-password
Symfony Password Encoder Utility
================================

 Type in your password to be encoded:
 > // パスワードを入力してEnter

 ------------------ ---------------------------------------------------------------------------------------------------
  Key                Value
 ------------------ ---------------------------------------------------------------------------------------------------
  Encoder used       Symfony\Component\Security\Core\Encoder\MigratingPasswordEncoder
  Encoded password   $argon2id$v=19$m=65536,t=4,p=1$d0E5RUNweWlFMWdyWVhubw$QfPYWiHKhw+I7S4bTWPAFj8gSiyismTWad4pHtY3/NA
 ------------------ ---------------------------------------------------------------------------------------------------

 ! &#91;NOTE] Self-salting encoder used: the encoder generated its own built-in salt.


 &#91;OK] Password encoding succeeded</code></pre>



<p>パスワードエンコードのコマンドで出力されたEncode passwordの値をデータベースのパスワードの項目へ登録します。</p>



<h4 class="wp-block-heading"><span id="toc13">確認</span></h4>



<p>ブラウザから以下のURLへアクセスし、データベースへ登録したメールアドレスとパスワード（暗号化前の値）を入力して確認します。</p>



<pre class="wp-block-code"><code>http:&#47;&#47;localhost/login</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.takeho.com/pog4353byfh9f9xakd0ya815mw2w4xn8/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
