Symfonyでコントローラのコードをリファクタリングする方法をやさしく解説!初心者向けガイド
生徒
「先生、Symfonyで作ったコントローラのコードがごちゃごちゃして読みにくくなってきたんですが、どうすればいいですか?」
先生
「それはリファクタリングのチャンスですね!」
生徒
「リファクタリング?なんか難しそうな言葉ですね…」
先生
「心配しなくて大丈夫。リファクタリングとは、“動きはそのままに、中身をキレイに整理すること”なんです。台所を掃除して、道具を使いやすく並べるような感じですね。」
生徒
「なるほど、コードも整理整頓って大事なんですね!やり方を教えてください!」
先生
「それでは、Symfonyでコントローラのリファクタリングをする方法を、やさしく解説していきましょう!」
1. リファクタリングとは?コードの「整理整頓」で効率アップ!
リファクタリングとは、プログラムが提供する「動作」や「機能」はそのままで、内部のソースコードを読みやすく、管理しやすい構造に書き換える作業のことです。
例えば、日々の生活で「机の上が散らかっていると、探し物に時間がかかる」のと同じように、プログラムも放置すると複雑になり、修正や機能追加に時間がかかるようになります。これを「コードの腐敗」と呼びますが、リファクタリングを行うことで、バグの発見が早まり、将来のメンテナンスが格段に楽になります。
料理のレシピで「塩を振る、胡椒を振る、醤油を入れる…」と毎回書く代わりに、「基本の味付けをする」という1つの工程にまとめるようなイメージです。
具体的なイメージを持つために、簡単なPHPの例を見てみましょう。まずは「リファクタリング前」の少し読みづらいコードです。
// リファクタリング前:何回も同じ計算が出てきて読みづらい
echo "リンゴの価格は " . (100 * 1.1) . " 円です。";
echo "バナナの価格は " . (150 * 1.1) . " 円です。";
echo "ミカンの価格は " . (80 * 1.1) . " 円です。";
次に、リファクタリングをして「税込み計算」を共通化したコードです。
// リファクタリング後:計算ルールを1箇所にまとめてスッキリ!
function addTax($price) {
return $price * 1.1;
}
echo "リンゴの価格は " . addTax(100) . " 円です。";
echo "バナナの価格は " . addTax(150) . " 円です。";
echo "ミカンの価格は " . addTax(80) . " 円です。";
このように、Symfonyのコントローラにおいても、複雑になった「条件分岐」や「データの計算処理」を整理することで、誰が見ても理解しやすい「美しい設計」へと進化させることができます。
2. リファクタリング前の例
まずは、少しごちゃごちゃした状態のコントローラを見てみましょう。
#[Route('/product/{id}', name: 'product_detail')]
public function detail(int $id): Response
{
$product = $this->getDoctrine()->getRepository(Product::class)->find($id);
if (!$product) {
throw $this->createNotFoundException('商品が見つかりません');
}
$priceWithTax = $product->getPrice() * 1.1;
return $this->render('product/detail.html.twig', [
'product' => $product,
'priceWithTax' => $priceWithTax,
]);
}
このままでも動きますが、いろんな処理が混ざっていて少し読みづらいですね。
3. リファクタリングのポイント
以下のような処理を整理しましょう:
- 共通処理(商品の取得)を専用の関数に分ける
- 計算処理(税計算)を別の関数に切り出す
こうすることで、コントローラ本体は「流れだけ」を書いて読みやすくなります。
4. リファクタリング後のコード
private function findProductOr404(int $id): Product
{
$product = $this->getDoctrine()->getRepository(Product::class)->find($id);
if (!$product) {
throw $this->createNotFoundException('商品が見つかりません');
}
return $product;
}
private function calculateTaxPrice(float $price): float
{
return $price * 1.1;
}
#[Route('/product/{id}', name: 'product_detail')]
public function detail(int $id): Response
{
$product = $this->findProductOr404($id);
$priceWithTax = $this->calculateTaxPrice($product->getPrice());
return $this->render('product/detail.html.twig', [
'product' => $product,
'priceWithTax' => $priceWithTax,
]);
}
リファクタリング後は、関数名を見るだけで何をしているか分かるようになりましたね!
5. Symfonyでよくあるリファクタリングのアイデア
- 長すぎる関数 → 小さく分割
- 同じコードが何度も登場 → 関数にまとめる
- HTMLテンプレートへの渡し方 → 配列名を整理する
- ベースコントローラを使う → 共通処理の集約
「読みやすいコード」は、後から自分やチームの仲間が見たときに理解しやすくなります。
6. 初心者でもできる!リファクタリングのコツ
リファクタリングは、小さなところから少しずつ始めるのがポイントです。
最初は「同じコードが2回以上出てきたら関数にまとめる」だけでも十分です。
慣れてきたら、ベースコントローラにまとめたり、サービスクラスに分ける方法もあります(今回は紹介しません)。
「キレイにすることは悪ではない」どころか、長く使うコードほど大事な考え方です。
まとめ
Symfonyを用いたWebアプリケーション開発において、コントローラーの肥大化は避けて通れない課題の一つです。初心者のうちは、一つのアクションメソッド内にデータベースの取得処理、バリデーション、計算ロジック、レスポンスの生成などを全て詰め込んでしまいがちです。しかし、今回の解説で学んだように、「リファクタリング」という工程を挟むことで、コードの可読性とメンテナンス性は劇的に向上します。
リファクタリングの第一歩は、「役割を分担させること」です。例えば、特定のIDからエンティティを取得して、存在しない場合に404エラーを投げる処理などは、複数のメソッドで使い回される可能性が高い共通処理です。これをプライベートメソッドに切り出すだけで、メインとなるアクションメソッドの見通しが良くなります。
また、Symfony独自の機能を活用したリファクタリングも非常に効果的です。例えば、引数にエンティティを指定して自動的にデータを取得する「ParamConverter」のような仕組みを使えば、さらにコードを短縮できます。
さらなるステップアップ:サービスへの切り出し
今回のリファクタリングでは、コントローラー内のプライベートメソッドへ処理を分割しました。しかし、さらにプロジェクトが大きくなると、コントローラー自体が長くなってしまいます。その場合は、「サービスクラス(Service Class)」を作成し、ビジネスロジックをコントローラーの外へ追い出すのがSymfonyの王道的な設計手法です。
以下のサンプルコードは、計算ロジックをコントローラーに残さず、依存注入(Dependency Injection)を意識した構成の一歩手前のイメージです。
// 商品関連の計算ロジックをまとめるイメージ
private function formatProductData(Product $product): array
{
return [
'id' => $product->getId(),
'name' => $product->getName(),
'price_tax' => $this->calculateTaxPrice($product->getPrice()),
'stock_status' => $product->getStock() > 0 ? '在庫あり' : '品切れ中',
];
}
#[Route('/api/product/{id}', name: 'api_product_detail')]
public function apiDetail(int $id): Response
{
// 共通化されたメソッドで取得
$product = $this->findProductOr404($id);
// 表示用データの整形を分離
$data = $this->formatProductData($product);
return $this->json($data);
}
このように、出力結果を整形する処理を分けるだけでも、修正が必要になった際に「どこを直せばいいか」が明確になります。例えば「消費税率が10%から変わった」という時も、calculateTaxPriceメソッド一箇所を直すだけで、アプリ全体の計算結果を修正できるのです。
リファクタリングで意識すべき3つのポイント
- DRY原則(Don't Repeat Yourself):同じことを二度書かない。重複したコードは一つにまとめましょう。
- 単一責任の原則:一つのメソッドには、一つのことだけをさせる。例えば「データ取得」と「メール送信」を一つのメソッドでやらないようにします。
- 直感的な命名:
$aや$tempといった変数名ではなく、$priceWithTaxのように、中身がすぐにわかる名前を付けましょう。
Symfonyの開発では、フレームワークが用意してくれている便利なコマンドやディレクトリ構造を最大限に活かすことが、美しいコードへの近道です。最初は少し面倒に感じるかもしれませんが、後からバグを探す時間や、新機能を追加する際の苦労を考えれば、リファクタリングに投資する時間は決して無駄にはなりません。
まずは今日書いた自分のコードを眺めてみて、「ここ、もっと短く書けないかな?」と自問自答するところから始めてみましょう。それがエンジニアとしての成長への大きな一歩になります。
生徒
先生、ありがとうございました!リファクタリングをした後のコードを見たら、パッと見て何が起きているかすごく分かりやすくなりました。
先生
そうでしょう!コードは「書く時間」よりも「読む時間」の方が圧倒的に長いんです。だから、未来の自分や他の開発者が読んだときに、ストレスなく理解できることが一番大切なんですよ。
生徒
確かに。さっきのリファクタリング前のコードだと、税金の計算とか404エラーの処理が混ざっていて、メインの「商品の詳細を表示する」っていう目的が埋もれてしまっていましたね。
先生
その通りです。プログラムの動作結果を確認することも大事ですが、Symfonyのデバッグツールバーなどを使って、リファクタリング前後でエラーが出ていないかを確認しながら進めるのがコツですよ。
生徒
質問なんですが、どれくらい細かく分けたらいいんでしょうか?細かく分けすぎると、逆にメソッドが増えすぎて迷子になりそうで……。
先生
いい質問ですね!目安としては、「その処理に名前を付けられるかどうか」です。例えば「税込み価格を計算する」という明確な名前が付けられるなら、それは一つのメソッドとして独立させる価値があります。逆に、2〜3行の単純な処理で、その場所でしか使わないなら無理に分けなくても良い場合もあります。
生徒
なるほど!「名前を付けられる単位」で考えるんですね。そういえば、リファクタリングをした後にブラウザで動作確認をしたら、見た目は全然変わっていないのに、コードの裏側がすごくスッキリしていて感動しました。
先生
それがリファクタリングの醍醐味です。「動作を変えずに構造を整える」。この感覚に慣れてくると、プログラミングがどんどん楽しくなりますよ。次は、今回作ったプライベートメソッドを、他のコントローラーからも使えるように「サービスクラス」へ移動させる方法にも挑戦してみましょうか!
生徒
はい!ぜひお願いします。Symfonyをもっと使いこなせるようになりたいです!