LoginSignup
4
5

More than 3 years have passed since last update.

Symfonyに触ったメモ

Posted at

どうしてSymfonyを?

既存システムを解析する案件が来る予定だったのでちょこちょこ調べた。
ざっくばらんなメモなので、通して読むよりトピック的に拾う方が正しい。

Symfonyに触れる環境を作る

SymfonyCastsの「Charming Development in Symfony 5」をなぞるのが手取り早い。
「03. Route, Controllers&Responses!」までやると、とりあえず動くようになる。

Symfony解析時の基本的な流れ

  1. routing.ymlでルーティング情報を確認する
  2. Controllerに格納されているXxxController.phpでルーティングに対応する関数を確認する
  3. 関数内の処理を解析する

フォルダパス

Sample\SampleBundle
├─Controller           // コントローラ。ルーティングに対応するのはここを参照。
├─DataFixtures         // 初期データ等を用意するデータフィクスチャ
│  └─ORM               // ORMで実行するデータフィクスチャ
├─DependencyInjection
├─Entity               // データを運搬するためのEntity。ORMとのマッピングもここ
├─Form                 // Entityをごにょごにょする(要調査)
├─Repository           // DBとのやりとりする処理を格納
├─Resources
│  ├─config
│  │  ├─config.yml     // 設定値を保存するyamlファイル
│  │  ├─routing.yml    // [重要]ルーティング情報を保存するyamlファイル
│  │  └─services.yml
│  ├─public
│  │  └─css            // CSS
│  └─views             // コントローラ名に対応するフォルダ。フォルダ内はtwigファイル。
│      └─AAA           // AAAコントローラに対応するtwigファイル
├─Tests                // 自動テストに使用するファイ
└─Twig
    └─Extensions

登録されているバンドルを確認する

以下のファイルにインストールされているバンドルの情報が記載される。

%アプリケーションのルートディレクトリ%/app/AppKernel.php

  • $bundleに対してnewされているものは状態を問わずに使われる
  • devと付いているものは開発環境で使われる
  • testと付いているものはテスト環境で使われる

バンドルのルーティングを確認する

以下のファイルに使用するバンドルのルーティングを記載したyamlの格納パスが記載される。

%アプリケーションのルートディレクトリ%/app/config/routing.yaml

上記ファイルパスを開いた例が以下である。

routing.yaml
SampleBundle:
    resource: "@SampleBundle/Resources/config/routing.yml"
    prefix:   /

resourceに設定されたファイルはファイルパスに直すと以下になる。

%アプリケーションのルートディレクリ%/src/%ネームスペース名%/%バンドル名%/Resources/config/routing.yaml

上記ファイルパスを開いた例が以下である。

routing.yaml
SampleBundle_homepage:
    pattern:  /
    defaults: { _controller: SampleBundle:Page:index }
    requirements:
        _method:  GET

SampleBundle_about:
    pattern:  /about
    defaults: { _controller: SampleBundle:Page:about }
    requirements:
        _method:  GET

こちらの例であれば、以下のルーティングが設定されている。

  • /GETでアクセスした際、PageControllerindexActionを呼び出す
  • /aboutGETでアクセスした際、PageControlleraboutActionを呼び出す

ルーティング

上記のルーティングの設定にコメントを付与すると以下になる。

routing.yaml(抜粋)
SampleBundle_homepage:
    pattern:  /                                        # URLパターン
    defaults: { _controller: SampleBundle:Page:index } # 呼び出すコントローラと関数
    requirements:
        _method:  GET                                  # リクエスト

URLパターンの一部をブラケット({})で括るとその部分はプレースホルダーとしてどのような値でもマッチする。
設定例は以下である。

パターンサンプル
SampleBundle_anyvalue:
    pattern:  /anyvalue/{value}
    defaults: { _controller: SampleBundle:Page:anyvalue }

上記設定では以下がマッチする。

  • %バンドルに設定したパス%/anyvalue/
  • %バンドルに設定したパス%/anyvalue/hoge
  • %バンドルに設定したパス%/anyvalue/foobar

また、上記例ではrequirements_methodが省略されているが、その場合、GETPOSTPUTDELETEいずれにもマッチする。

URLに含まれる不要なスラッグ

以下のようなルーティングとURLを考える。

slugサンプル
SampleBundle_slugsample:
    pattern:  /{id}/{value}
    defaults: { _controller: SampleBundle:Page:slugsample }
    requirements:
        _method:  GET
        id: \d+
URL
http://somedomain/appname/1/this_is_a_slug

上記例ではrequirementsid(=1)は含まれているが、slugは含まれていない。
このような場合、URLから内容がわかるため、SEOを期待している。
設定する場合は、登録時にタイトルの非ASCII文字を-に置き換えてDBに格納すると良い。

テンプレート

基本的にPHPtwigが使われる。
テンプレートの継承とデザインは「3レベル継承(three-level template inheritance)」アプローチが推奨されている。

twigであればテンプレートの格納先は以下のどちらかになる。

  • %アプリケーションのルートディレクリ%/app/Resources/views/xxxx.html.twig
  • %アプリケーションのルートディレクリ%/src/%ネームスペース名%/%バンドル名%/Resources/views/xxxx.html.twig

リンク

twigを使用している場合のリンクは以下のようにルーティングで設定した名前となる。

sample.html.twig
{% block navigation %}
<nav>
    <a href="{{ path('SampleBundle_homepage') }}">Home</a>
</nav>
{% endblock %}

間違っても以下のようにURLで設定してはいけない
これはSymfonyのルーティングを無視することになるため、正常に動作しない。

sample.html.twig
{% block navigation %}
<nav>
    <!-- DO NOT USE -->
    <a href="/">Home</a>
</nav>
{% endblock %}

EntityとForm

値を格納する最小単位がEntityである。
Entityでは以下のような要素を格納する。

  • 変数
  • 変数へのアクセサ(変数hogeへのgetHoge/setHoge)
  • バリデーション(loadValidatorMetadata)

FormBuilderInterfaceを使用したり、Controllerで設定する際に使用されるのがFormである。
AbstractTypeをextendsしていることがある。

ルーティング設定例

ルーティングを設定するには以下の方法がある。
どちらを軸に設定されているか確認する。

  1. routes.ymlで設定する
  2. アノテーションで設定する

routes.yamlで設定する

routes.ymlの配置先は以下の通り。

%プロジェクトのフォルダ%/config/routes.yml

開くとデフォルトで以下のようになっている。

routes.yml
#index:
#    path: /
#    controller: App\Controller\DefaultController::index

行頭の「#」はコメントアウトを示す。
pathはURLのパスを、controllerは対応するコントローラを示す。
上記のコメントアウトを外した場合、「/」で「%プロジェクトのフォルダ%/src/Controller/DefaultController.php」の「index」が呼ばれることになる。

アノテーションで設定する

パスをアノテーションで関数単位で設定する。
以下の例では「/」で画面に「sample response」と表示される。

SampleController.php
<?php

namespace App\Controller;

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

class SampleController
{
    /**
     * @Route("/")
     */
    public function homepage()
    {
        return new Response('sample response');
    }
}
?>

設定されているパスを一覧で表示する

terminalでプロジェクトのフォルダを開き、以下のコマンドを叩く。

$ php bin/console debug:router

以下のように表示される。

terminal
 ----------------------- -------- -------- ------ -------------------------- 
  Name                    Method   Scheme   Host   Path                      
 ----------------------- -------- -------- ------ -------------------------- 
  _preview_error          ANY      ANY      ANY    /_error/{code}.{_format}  
  app_sample_homepage     ANY      ANY      ANY    /                                
 ----------------------- -------- -------- ------ -------------------------- 

バリデーション

入力値をDB登録前に検証する。
Entity内に設定することで検証が行われる。

バリデータを使う際は以下のように使う制約をuse命令文で追加する

use Symfony\Component\Validator\Mapping\ClassMetadata;

Doctrine2

SQLではなくDoctrine Query Language(DQL)を使用する。
ORMとして動作し、各DBとのやりとりを行い、データの永続化に寄与する。
Entityporotectedな変数を宣言し、アノテーションでマッピングを行う。

Sample.php(変数宣言)
namespace Sample\SampleBundle\Entity;

class Sample
{
    protected $id;
    protected $value1;

    // 後述のgetter/setterは省略
}
Sample.php(ORM)
namespace Sample\SampleBundle\Entity;

use Doctrine\ORM\Mapping as ORM; // Doctrineを読み込み

//
/**
 * @ORM\Entity
 * @ORM\Table(name="sample")
 */
class Sample
{
    // オートインクリメントするIDの設定
    /**
    * @ORM\Id
    * @ORM\Column(type="integer")
    * @ORM\GeneratedValue(strategy="AUTO")
    */
    protected $id;

    // 文字列型のvbalue1の設定
    /**
    * @ORM\Column(type="string")
    */
    protected $value1;
}

実際に設定する場合は、インスタンスを生成して値を入れる。

$sample = new Sample();
$sample->setValue1("val1");
// idは自動歳晩なのでset不要

Doctrine2を使用してSQLを流すこともできるが、データベース抽象を使用しないため推奨されない
SQLを実行したい場合はQueryBuilderを使用するとよい。

QueryBuilderSample.php
// QueryBuilder以下を表現
//  SELECT 'hoge', 'created' FROM SampleBundle ORDER BY 'created' DESC;
$em = $this->getDoctrine()->getManager();

$records = $em->createQueryBuilder()
              ->select('hoge')
              ->from('SampleBundle:Sample',  'hoge')
              ->addOrderBy('hoge.created', 'DESC')
              ->getQuery()
              ->getResult();

クエリー実行は以下の理由からコントローラ内で行わないこと

  • アプリケーション内で同一のクエリーを実行する場合、再利用ができない
  • コードが重複しする(DRY原則に反する)
  • 分離することにより、クエリー単体のテストが実行可能となる

⇒分離する際はDoctrine2 Repositoriesを使用する

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5