Skip to content

Commit 02d754e

Browse files
committed
Merge remote-tracking branch 'origin/master' into best-practices-controllers
2 parents 7fde34c + a155296 commit 02d754e

18 files changed

+2465
-1
lines changed

best_practices/business-logic.rst

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
.. note::
2+
3+
* 対象バージョン:2.3以降
4+
* 翻訳更新日:2014/11/05
5+
6+
アプリケーションのビジネスロジックを整理する
7+
============================================
8+
9+
コンピュータのソフトウェアの世界では、データがどのように作られ、表示され、保存され、変更されるかを決める
10+
現実世界のビジネスルールを実装したプログラムを **ビジネスロジック** あるいはドメインロジックと呼びます。
11+
( `完全な定義`_ を読む)
12+
13+
Symfony のアプリケーションでは、ビジネスロジックは、フレームワーク(例えばルーティングやコントローラの
14+
ような)に依存せず、自由に書くことができます。
15+
サービスとして使われる、ドメインのクラス・ Doctrine のエンティティ・通常の PHP のクラスは、ビジネスロジック
16+
の一例です。
17+
18+
ほとんどのプロジェクトにおいて、開発者は全てを ``AppBundle`` に置くべきです。AppBundleの中では、ビジネス
19+
ロジックを整理するための、どんなディレクトリでも作ることができます。
20+
21+
.. code-block:: text
22+
23+
symfoy2-project/
24+
├─ app/
25+
├─ src/
26+
│ └─ AppBundle/
27+
│ └─ Utils/
28+
│ └─ MyClass.php
29+
├─ vendor/
30+
└─ web/
31+
32+
クラスをバンドルの外に置く
33+
--------------------------
34+
35+
しかし、ビジネスロジックをバンドルの中に入れておく技術的な理由は何もありません。 ``src`` ディレクトリの
36+
下に好きな名前空間を定義して、クラスをそこに置くこともできます。
37+
38+
.. code-block:: text
39+
40+
symfoy2-project/
41+
├─ app/
42+
├─ src/
43+
│ ├─ Acme/
44+
│ │ └─ Utils/
45+
│ │ └─ MyClass.php
46+
│ └─ AppBundle/
47+
├─ vendor/
48+
└─ web/
49+
50+
.. tip::
51+
52+
``AppBundle`` を使うことをお勧めするのは簡単さのためです。バンドルの中に必要なものと
53+
バンドルの外でも問題ないものがよく理解できている場合は、クラスを ``AppBundle`` の外に
54+
置いても構いません。
55+
56+
サービス:名付けと形式
57+
----------------------
58+
59+
ブログアプリケーションに、 "Hello World" のような投稿タイトルを "hello-world" のようなスラグに変換する
60+
ユーティリティが必要だとします。スラグは、投稿のURLの一部として使われます。
61+
62+
新しい ``Slugger`` クラスを ``src/AppBundle/Utils/`` ディレクトリの配下に作り、下記のような ``slugify()``
63+
メソッドを実装してみましょう。
64+
65+
.. code-block:: php
66+
67+
// src/AppBundle/Utils/Slugger.php
68+
namespace AppBundle\Utils;
69+
70+
class Slugger
71+
{
72+
public function slugify($string)
73+
{
74+
return preg_replace(
75+
'/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string)))
76+
);
77+
}
78+
}
79+
80+
次に、このクラスのための新しいサービスを定義します。
81+
82+
.. code-block:: yaml
83+
84+
# app/config/services.yml
85+
services:
86+
# サービス名は短くしましょう
87+
slugger:
88+
class: AppBundle\Utils\Slugger
89+
90+
伝統的に、サービスの名前は、衝突を避けるためにクラス名とクラスの場所を組み合わせたものでした。
91+
そうすると、このサービスは ``app.utils.slugger`` と呼ばれる *はず* です。しかし、短い名前を使うことで、
92+
コードの読みやすさと使いやすさは向上するでしょう。
93+
94+
.. best-practice::
95+
96+
アプリケーションのサービス名は可能な限り短くしましょう。一単語になるのが理想的です。
97+
98+
これで ``AdminController`` のようなどんなコントローラーからでも slugger を利用できるようになりました。
99+
100+
.. code-block:: php
101+
102+
public function createAction(Request $request)
103+
{
104+
// ...
105+
106+
if ($form->isSubmitted() && $form->isValid()) {
107+
$slug = $this->get('slugger')->slugify($post->getTitle()));
108+
$post->setSlug($slug);
109+
110+
// ...
111+
}
112+
}
113+
114+
サービス定義:YAML形式
115+
----------------------
116+
117+
前のセクションでは、 YAML がサービスを定義するのに使われていました。
118+
119+
.. best-practice::
120+
121+
サービスを定義するときは YAML 形式を使いましょう。
122+
123+
これには異論があるでしょうが、経験上、開発者の間では YAML と XML が半々で使われており、ほんの少し YAML
124+
のほうが好まれています。
125+
どちらの形式も機能は同じなので、どこまでも個人の好みの問題です。
126+
127+
新人にもわかりやすく、シンプルな YAML をお勧めしますが、好きな形式を使って構いません。
128+
129+
サービス定義:クラス名をパラメータにしない
130+
------------------------------------------
131+
132+
前の例で、サービスを定義するとき、クラス名をパラメータとして定義していないことにお気付きかもしれません。
133+
134+
.. code-block:: yaml
135+
136+
# app/config/services.yml
137+
138+
# クラス名をパラメータにしてサービスを定義
139+
parameters:
140+
slugger.class: AppBundle\Utils\Slugger
141+
142+
services:
143+
slugger:
144+
class: "%slugger.class%"
145+
146+
この使い方は煩雑で、アプリケーションのサービスには全く必要ありません。
147+
148+
.. best-practice::
149+
150+
アプリケーションのサービスクラス名をパラメータとして定義するのは止めましょう。
151+
152+
この使い方はサードパーティのバンドルから誤って取り入れられたものです。 Symfony がサービスコンテナ機能を
153+
実装したとき、開発者の中にはこのテクニックによってサービスを上書きできるようにした人もいました。
154+
しかし、クラス名を変更しただけでサービスを上書きするのは非常に稀なユースケースです。というのも、大抵の場合、
155+
新しいサービスには、上書きされるサービスとは違うコンストラクタ引数があるからです。
156+
157+
永続化レイヤーを利用する
158+
------------------------
159+
160+
Symfony は、 各 HTTP リクエストに対する HTTP レスポンスを作ることだけを担当する HTTP のフレームワークです。
161+
そのため、 Symfony は永続化レイヤー(データベースや外部API)にアクセスする方法を提供していません。開発者は
162+
好きなライブラリやデータ保存方法を選ぶことができます。
163+
164+
実際には、 Symfony アプリケーションの多くは `Doctrine`_ に依存しており、エンティティやレポジトリを
165+
使ってモデルを定義しています。
166+
ビジネスロジックと同じく、 Doctrine のエンティティも ``AppBundle`` に配置すると良いでしょう。
167+
168+
一例として、サンプルのブログアプリケーションで定義した3つのエンティティがあります。
169+
170+
.. code-block:: text
171+
172+
symfony2-project/
173+
├─ ...
174+
└─ src/
175+
└─ AppBundle/
176+
└─ Entity/
177+
├─ Comment.php
178+
├─ Post.php
179+
└─ User.php
180+
181+
.. tip::
182+
183+
もちろん、独自の名前空間で ``src/`` の直下にエンティティを置くこともできます。
184+
185+
Doctrine のマッピング
186+
~~~~~~~~~~~~~~~~~~~~~
187+
188+
Doctrine のエンティティは、データベースに保存することができるプレーンな PHP オブジェクトです。
189+
Doctrine は、クラスに対して定義されたマッピングメタデータによってエンティティを扱います。マッピングメタデータ
190+
を定義するには YAML, XML, PHP, アノテーション形式が利用できます。
191+
192+
.. best-practice::
193+
194+
Doctrine エンティティのマッピングにはアノテーションを使いましょう。
195+
196+
アノテーションは、マッピングを定義したり探したりするのに、現在のところ最も便利で素早く使える形式です。
197+
198+
.. code-block:: php
199+
200+
namespace AppBundle\Entity;
201+
202+
use Doctrine\ORM\Mapping as ORM;
203+
use Doctrine\Common\Collections\ArrayCollection;
204+
205+
/**
206+
* @ORM\Entity
207+
*/
208+
class Post
209+
{
210+
const NUM_ITEMS = 10;
211+
212+
/**
213+
* @ORM\Id
214+
* @ORM\GeneratedValue
215+
* @ORM\Column(type="integer")
216+
*/
217+
private $id;
218+
219+
/**
220+
* @ORM\Column(type="string")
221+
*/
222+
private $title;
223+
224+
/**
225+
* @ORM\Column(type="string")
226+
*/
227+
private $slug;
228+
229+
/**
230+
* @ORM\Column(type="text")
231+
*/
232+
private $content;
233+
234+
/**
235+
* @ORM\Column(type="string")
236+
*/
237+
private $authorEmail;
238+
239+
/**
240+
* @ORM\Column(type="datetime")
241+
*/
242+
private $publishedAt;
243+
244+
/**
245+
* @ORM\OneToMany(
246+
* targetEntity="Comment",
247+
* mappedBy="post",
248+
* orphanRemoval=true
249+
* )
250+
* @ORM\OrderBy({"publishedAt" = "ASC"})
251+
*/
252+
private $comments;
253+
254+
public function __construct()
255+
{
256+
$this->publishedAt = new \DateTime();
257+
$this->comments = new ArrayCollection();
258+
}
259+
260+
// getters and setters ...
261+
}
262+
263+
全てのメタデータ定義形式に同じ機能があり、何度も書いたようにどの形式を使うかは開発者の自由です。
264+
265+
データフィクスチャー
266+
~~~~~~~~~~~~~~~~~~~~
267+
268+
Symfony にはデフォルトではデータフィクスチャー機能は存在しないため、フィクスチャーを扱うためには下記のコマンドを
269+
実行して DoctrineFixturesBundle をインストールする必要があります。
270+
271+
.. code-block:: bash
272+
273+
$ composer require "doctrine/doctrine-fixtures-bundle"
274+
275+
バンドルを ``AppKernel.php`` で有効化します。その際、 ``dev`` 環境と ``test`` 環境だけにしてください。
276+
277+
.. code-block:: php
278+
279+
use Symfony\Component\HttpKernel\Kernel;
280+
281+
class AppKernel extends Kernel
282+
{
283+
public function registerBundles()
284+
{
285+
$bundles = array(
286+
// ...
287+
);
288+
289+
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
290+
// ...
291+
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
292+
}
293+
294+
return $bundles;
295+
}
296+
297+
// ...
298+
}
299+
300+
301+
単純さを保つために、 `フィクスチャークラス`_ は *1つ* だけ作ることをおすすめします。クラスが大きくなりすぎる
302+
場合はもっとたくさんのフィクスチャークラスを作っても構いません。
303+
304+
少なくとも1つのフィクスチャークラスがあり、データベースへのログイン情報が正しく設定されているのを確認したら、
305+
下記のコマンドを実行するとフィクスチャーを読み込ませることができます。
306+
307+
.. code-block:: bash
308+
309+
$ php app/console doctrine:fixtures:load
310+
311+
Careful, database will be purged. Do you want to continue Y/N ? Y
312+
> purging database
313+
> loading AppBundle\DataFixtures\ORM\LoadFixtures
314+
315+
316+
コーディング規約
317+
----------------
318+
319+
Symfony のソースコードは、 PHP コミュニティで定められた `PSR-1`_ と `PRS-2`_ のコーディング規約に
320+
従っています。詳しくは `Symfonyのコーディング規約`_ を読んでください。
321+
または、`PHP-CS-Fixer`_ コマンドを使うこともできます。PHP-CS-Fixerはコードベース全体のコーディング規約を
322+
ほんの数秒で修正することができるコマンドラインツールです。
323+
324+
.. _`完全な定義`: http://en.wikipedia.org/wiki/Business_logic
325+
.. _`Doctrine`: http://www.doctrine-project.org/
326+
.. _`フィクスチャークラス`: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
327+
.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
328+
.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
329+
.. _`Symfonyのコーディング規約`: http://symfony.com/doc/current/contributing/code/standards.html
330+
.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer
331+
332+
.. 2014/11/05 77web 0f1cf411a0bb630205ce4ac2c5e75d237384f8dc

0 commit comments

Comments
 (0)