Symfonyについて。。。動画でざっくりと全体を掴むメモ_φ(・_・

Symfony入門として解説されていたYoutube動画。2年前と少し古いですが、Symfonyについてまずは全体像をつかみたくて視聴しました。

以下、動画をみて自分でメモをとったものです。アウトプット用で自分なりにメモしているので、間違っているところもあるかもしれません(細かなニュアンスなども)。もし同じようにSymfonyの情報を得たい方は動画をみていただいた方が良いかと思います。

Symfonyとは、Webアプリを作るためのフレームワーク
アーキテクチャ・・・SymfonyはMVCタイプのフレームワーク

クライアント(URL) -> router -> Controller(アクション 例search,show,…etc)
-> Model -> DB -> Model -> View

router・・・どのControllerのどのアクションに割り振るべきかを決める、呼び出しを実行する

Controller・・・必要に応じてデータベースへのアクセスを行う。データベースを接続する際は、Modelクラスを経由する。Modelクラスからデータを受け取って、最終的にはHTMLのデータをレンダリングする。
HTMLのデータをレンダリングするのはViewクラスが担当。

Synfonyの採用技術
・Model ⇨Doctrine
・View ⇨Twing

Symfonyのバージョン
・1系だけは別物
・2系以降は同系統

Symfonyとの付き合い方
・日本語情報が少ないので英語と向き合う

シングルトンとは
・GoFのデザインパターンの一種
・常に同じインスタンスを返す

※Gang Of Fourのデザインパターンとは「よく見かける機能要件(解決したい問題)」と「各機能要件に対応する設計方針」を23種類に整理したもの。以下は参考サイト。Symfonyからはちょっと脱線します。

DIコンテナとは(最重要)
・サービス(便利なクラス)を登録しておく場所
・シングルトンで取り出される

DIコンテナ(例 Logger, HttpClient, SlackClient, PaymentService)
Logger -> Contoroller(呼び出し側)
SlackClient -> ロジッククライアント(呼び出し側)

SymfonyのDIコンテナ
・SymfonyはDIコンテナを搭載
・DIコンテナにはLoggerやEntityManager(データベースへの接続を簡単にしてくれる)など便利なサービスが用意されている
・自作のサービスクラスを登録することも可能

SymfonyのDIコンテナにはあらかじめ、いろんなサービスが登録されている
DIコンテナからサービス(クラス)を呼び出す時はService IDを使う

DIコンテナ経由でサービスを呼び出す

DIコンテナへサービスを登録
・services.ymlで登録
・ymlの他に、xmlやphpファイル形式も可

SymfonyのDIコンテナ
・今説明したのはバージョン2時代の話(基本)
・最近のバージョンでは、もっと便利になっている

DIコンテナからサービスを呼び出す
・5系では、controllerの引数に定義するだけでサービスを自動で差し込んでくれる

DIコンテナへサービスを登録
・5系ではサービス定義不要。自動で登録される。

Symfonyのチュートリアル

Controller

SymfonyのControllerでは、アノテーションというものを使ってルーティングを行う。
Twigのレンダリングを行う時は「$this->render()」 というメソッドを使う。

実際に、せお丸さんのコードとは違いますが、公式サイトからContorollerについて引っ張ってきたコードが下記です。

// src/Controller/LuckyController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class LuckyController
{
    #[Route('/lucky/number/{max}', name: 'app_lucky_number')]
    public function number(int $max): Response
    {
        $number = random_int(0, $max);

        return new Response(
            '<html><body>Lucky number: '.$number.'</body></html>'
        );
    }
}

Controllerの説明については、公式ドキュメントでもせお丸さんの解説でもすぐには理解が出来なかったので(私の理解力のなさ。。。orz)、ChatGPTに分かりやすい説明を求めました。以下にまとめます。

SymfonyのControllerとは、Webアプリケーションのリクエストとレスポンスを処理するコードを含むクラスです。つまり、ユーザーからのリクエストを受け取り、それに応じた処理を行い、レスポンスを返す役割を持っています。

Controllerは、ルーティング機能と連携して動作します。ルーティングは、リクエストされたURLに対して、どのControllerが処理を担当するかを決定します。例えば、「/login」のURLに対しては、LoginControllerが処理を担当するように設定することができます。

Controllerの処理は、アノテーションや設定ファイルを使用して、どのHTTPメソッド(GET、POST、PUT、DELETEなど)に対応するかを定義することができます。また、Controllerは、ビューレンダリングやデータベースアクセスなど、アプリケーションの機能に応じてさまざまなタスクを実行することができます。

例えば、ログインフォームから送信されたユーザー名とパスワードを処理するControllerの例を示します。

<?php
// LoginController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class LoginController extends AbstractController
{
    public function login(Request $request): Response
    {
        $username = $request->request->get('username');
        $password = $request->request->get('password');

        // ユーザー名とパスワードが正しいかどうかを確認する処理

        return $this->render('login.html.twig');
    }
}

この例では、LoginControllerクラスにはlogin()メソッドがあります。このメソッドは、POSTメソッドで送信されたユーザー名とパスワードを処理するために、Requestオブジェクトを引数に取ります。そして、ユーザー名とパスワードを取得し、認証処理を実行します。最後に、Twigテンプレートを使用してログインフォームの表示をレスポンスとして返します。

以上が、SymfonyのControllerについて簡単に説明した内容です。詳しい使い方や、より複雑な機能については、公式ドキュメントを参照することをおすすめします。

ChatGPTにきいてみた

View(Twig)

SymfonyのViewでは、Twigと呼ばれるViewのフレームワークが使われている。
・extendsを使うと、テンプレートの継承や埋め込みができるようになる
・基本的にHTMLを書き込める
・変数を書きたいときは{{ }} を用いて書く
・if文やfor文を使うことができる
☆詳しい書き方は「Twig 書き方」で調べると良い

以下は、実際に公式ドキュメントから引っ張ってきたコード

<!DOCTYPE html>
<html>
    <head>
        <title>Welcome to Symfony!</title>
    </head>
    <body>
        <h1>{{ page_title }}</h1>

        {% if user.isLoggedIn %}
            Hello {{ user.name }}!
        {% endif %}

        {# ... #}
    </body>
</html>

Twig の構文は、次の 3 つの構造に基づいています。

  • {{ ... }}、変数の内容または式の評価結果を表示するために使用されます。
  • {% ... %}、条件付きやループなどのロジックを実行するために使用されます。
  • {# ... #}、テンプレートにコメントを追加するために使用されます (HTML コメントとは異なり、これらのコメントはレンダリングされたページには含まれません)。
https://symfony.com/doc/current/templates.html

公式サイトより、3つの構造について引用しました。
他にも詳しい説明があるので、もっと詳細が知りたい場合は公式サイトをのぞきにいきます。

Model(Entityクラス)

Entityクラスは、1インスタンスがDB1レコードに紐づくクラスのこと。
・このようなクラスのことをORマッパーという。

・ORマッピングをしてくれるのがドクトリンというモジュール。

以下、ChatGPTに。。。(略)。。

SymfonyのModelとは、データベースとのやりとりを担当する部分で、Entityクラスと呼ばれるクラスを使用します。Entityクラスは、データベースのテーブルに対応するクラスであり、各フィールドをプロパティとして持ちます。また、Doctrine ORM(Object-Relational Mapping)を使用して、データベースとEntityクラスのマッピングを行います。

Doctrine ORMは、データベースのテーブルをEntityクラスにマッピングするためのライブラリです。ORMは、Object-Relational Mappingの略で、オブジェクト指向プログラミングとリレーショナルデータベースを組み合わせて使用する際に、オブジェクトとデータベースの変換を自動的に行うことができます。

ORMを使用することで、データベースのテーブルを操作する際に、SQLクエリを直接書く必要がなくなります。代わりに、Entityクラスを操作することで、データベースに対する操作を行うことができます。例えば、以下のようなEntityクラスを定義することができます。

<?php
// src/Entity/Book.php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="books")
 */
class Book
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $title;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $author;

    // ゲッターセッターなどのメソッドは省略
}

このEntityクラスは、booksテーブルに対応しています。Bookクラスの各プロパティは、booksテーブルの各カラムに対応しています。例えば、$titleプロパティは、booksテーブルのtitleカラムに対応しています。

以上が、SymfonyのModel(Entityクラス)、ドクトリンとORマッパーについて簡単に説明した内容です。詳しい使い方や、より複雑な機能については、公式ドキュメントを参照することをおすすめします。

ChatGPTにきいてみた

Form

Typeクラスにフォームの内容を定義する
・SymfonyでFormを作るときはTypeクラスを用いる
・buildFormというメソッドの中に$builderというフォームビルダーを使って、フォームの部品をどんどん追加していく。細かい作り方は、公式ドキュメントをみながら作っていく

使用法

Symfony フォームを操作する際の推奨ワークフローは次のとおりです。

  1. Symfony コントローラまたは専用のフォーム クラスを使用してフォームを構築します。
  2. ユーザーが編集して送信できるように、テンプレートでフォームをレンダリングします。
  3. フォームを処理して送信されたデータを検証し、それを PHP データに変換して、何かを行います (データベースに保存するなど)。

これらの各手順については、次のセクションで詳しく説明します。例を理解しやすくするために、これらの例はすべて、「タスク」を表示する小さな Todo リスト アプリケーションを作成していることを前提としています。

https://symfony.com/doc/current/forms.html

フォームの種類

最初の Symfony フォームを作成する前に、「フォーム タイプ」の概念を理解することが重要です。他のプロジェクトでは、「フォーム」と「フォーム フィールド」を区別するのが一般的です。Symfony では、それらはすべて「フォーム タイプ」です。

  • 単一の<input type="text">フォーム フィールドは「フォーム タイプ」です (例TextType: )。
  • 住所を入力するために使用されるいくつかの HTML フィールドのグループは、「フォーム タイプ」です (例: PostalAddressType)。
  • <form>ユーザープロファイルを編集するための複数のフィールドを持つ全体は、「フォームタイプ」です (例UserProfileType: )。

最初は戸惑うかもしれませんが、すぐに自然に感じるようになります。さらに、コードを簡素化し、フォーム フィールドの「構成」と「埋め込み」をより簡単に実装できるようにします。

Symfony が提供するフォーム タイプは数十種類あり、独自のフォーム タイプを作成する こともできます。

https://symfony.com/doc/current/forms.html

データ登録処理

  1. 入力フォーム表示
  2. Submitされる

ControllerやModel,Formを組み合わせた、データ登録の一連の処理をみていく
入力フォームで内容を打ちこんで、submitボタンを押したらFormがsubmitされて、Controllerで内容が登録されるというベターな処理を例に挙げる

今回は、taskControllerのnewアクション
この中でtaskというEntityを新規登録していく
まずは

$task = new Task();

でEntityをnewする。
これから1レコードインサートしたいので、Entityクラスをnewする。
次にFormを作る。

$form = $this->createForm(TaskType::class, $task);

....(略)....

$this->createFormを使って、◯◯Typeクラス(TaskType)を指定。
この◯◯Typeクラスに渡すデータとして、先ほど作ったEntityを指定。
で、(略)部分は一旦飛ばして、
まずは単純に、Twigのレンダリングを行う。

return $this->render('task/new.html.twig', [
  'form' => $form->createView(),
]);

Twigには、先ほど作ったFormを渡してあげる。
$form->createView()を呼ぶと、いい感じにフォームを作ってくれる。
ここまでが、「1. 入力フォーム表示」までの説明。

次に「2. Submitされる」部分の説明。該当は上で(略)した部分。
まずは、$requestというデータを$formに渡している。

$form->handleRequest($request);
if( 略 ){
  ...(略)...
}

↑の$requestというのは、submitされた$requestデータが↓のRequestというクラスに入ってくる。
それを↑のhandleRequestで呼んで、↑の$formに対して$requestというデータをバインド(差し込み)させてあげている。

class ...(略)...
{
  public function new(Request $request): Response
  {
     ...(略。ここでフォームの内容が正しければ、この部分でデータベースの登録処理が進んでいく)...
  }
}

データベースの登録をする時はEntityクラスを呼び出す必要がある。
先ほど$taskというEntityのインスタンスのクラスを作って、$formに差込をしていた。
$taskというEntityに対してhandle Requestで$requestのデータ内容が流し込みされる。

結果的に、フォームに入力されたデータが$taskというEntityに差し込まれたことになる。

「$form->getData();」を呼ぶことで、データ入りのEntityクラスをgetできる。

次に、Symfonyでデータ登録をする時は、$entityManagerを使う。
5系ではentityManagerという引数を持たせれば、DIコンテナでサービスがインジェクションされる。

$entityManagerにEntityクラスである$taskを認識させてあげる必要がある。
persistというメソッドにEntityのインスタンスを渡してあげる(ここでは$task)。
これでdoctrineがEntityを認識できる。

最後に$entityManagerのflushメソッドで確定処理をする。
コミットみたいなもの。
データベースに対してインサート文が流れる。

ここまで、まとめてControllerのコードを書く↓
※これはおそらく2系の書き方だと思います。動画内では2系の書き方とは言っていないので注意※

class TaskController extends AbstractController
{
  public function new(Request $request): Response
  {
    $task = new Task();
    $form = $this->createForm(TaskType::class, $task);

    $form->handleRequest($request);
    if($form->Submitted() && $form->isVaild()){
      $task = $form->getData();

      $entityManager = $this->getDoctrine()->getManager();
      $entityManager->persist($task);
      $entityManager->flush();

      return $this->redirectToRoute('task_seccess');
    }
    return $this->render('task/new.html.twig', [
      'form' => $form->createView(),
     ]);
  }
}

Controllerのコードは大体、こんな感じになる。

データの参照(SELECT)

Symfonyでデータを参照する時もdoctrine(ドクトリン)を使う。
getRepositoryの引数には、呼び出したいEntityを指定する。

$repository = $this->getDoctrine()->getRepository(Product::class);


そのクラスのデータを取り出したい時は、「$repository->find」にid指定することで取り出すことができる。

// 例
$product = $repository->find($id);

idではなくnameなどで指定したいときは「findOneBy」メソッドを使う。

// 例
$product = $repository->findOneBy(['name' => 'KeyBord']);

このように、SymfonyではgetRepositoryというものを行なって、Controllerから$repository->findとよんでid指定することで、(Modelを経由して)データベースに情報を取りにいくことができる。

なお、データベースで取得したレコードの、1レコードはEntityクラスの1インスタンスとなって返ってくる(単純なORマッパー)。そのため、Entityクラスで書いていい内容は、データベースのもっているカラムを、メンバー変数でもつ、ぐらいしか書いてはいけない。

id指定や何かしらのカラム指定ではなく、もっと複雑な条件で情報を取りに行きたい時は、Entityクラスではなく、別にRepositoryクラスというものを作って、そこに処理を書いていく。
複雑なSQL文を書きたくなったら、EntityクラスではなくてRepositoryクラスの(SQL置き場)に書く。

Repositoryクラス

Repositoryクラスには、好きなメソッドを定義することができる。

このメソッドの中で$entitymanagerをゲットして、$entitymanagerの「$entityManager->createQuery()」を使って、SQLを書いていく。

SQL言語ではなくて、正確にはDQL(ドクトリン言語)。⇨Symfonyのマニュアルを見ながら書いていく。

独自のメソッドの呼び出し方は動画にコードあり。

Symfonyのアーキテクチャ

URLが指定されると「router」に処理がやってくる。
routerはControllerのアノテーションで指定するものだった。
ちなみに、Controllerのアノテーション以外にも、yamlだったりXML5でrouterを管理することもできる。

次は「Controller」にやってくる。
DIコンテナから使いたいサービスを呼び出して、処理をすすめる。
このControllerにはごちゃごちゃちとロジックを書くのはやめる。
Controllerはrequestを受けてresponseを返す役割をするもの。
できるだけ薄く処理を保ったままコードを書いた方が綺麗になる。

では例えば”ログイン処理”などのごちゃごちゃした処理をかく場合はどうするか?その場合は、”Service”などの自分で決めたサービスを作ってDIコンテナに登録しておく。
ログインサービスというのをControllerで呼び出して、Controllerはこのログインサービスに対して処理を任せる。
ごちゃごちゃした処理は、サービスクラスに任せる。
と、コードが綺麗になっていく。

必要に応じて、ModelのEntity(ORマッパー)やRepository(SQL置き場)にアクセスする。
Controllerから直接、Modelにアクセスすることも可能です。
ちなみにSymfonyでは、ModelとはORマッパーのEntityとSQL置き場にあるRepositoryのふたつある、と覚えておく。
※人によっては、サービスクラスもModelと考えていることもある。そういう解釈の仕方もある。

Symfony学習のコツ

・DIコンテナを理解すること
・英語から逃げないこと
 あえて英語で検索、英語で質問!
Symfony学習はこれが全て!!

この記事を書いた人