LoginSignup
41
34

More than 5 years have passed since last update.

RailsユーザーだけどPHP/Symfony触ってみた

Last updated at Posted at 2016-03-22

もうすぐPHPとSymfonyを使うかもしれないので、雰囲気をつかむために触ってみました。PHPからして初めてなので、軽くそこも勉強しつつ。

Symfonyは、フルスタックでMVCなWebアプリケーションフレームワークなんですって。

こんなん作る

最低限のMVCを組み込める以下のようなWebアプリを、RailsとSymfonyで作り比べてみることにしました。

ちなみに私、Railsユーザって言ってもそんなにRails使い込んでるわけではないですが、まあいい比較対象かなと。

コメントを保存するボタン、保存したコメントのリスト表示、全コメントを削除できるリンクのあるアプリです。画面は1つだけ。

rails2.png

Railsで

とりあえずRailsで作ってみました。

環境

Cloud9の初期環境のままです。
Cloud9は、クラウド上で動作するIDEです。ワークスペース1個作ると開発環境が一通り揃ったUbuntu1つもらえるので、とりあえず何か動かしたいときは便利。

  • Ubuntu 14.04.3 LTS
  • ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
  • Rails 4.2.5

作って動かす

アプリcommenter-rを新規作成、commentsという名前でControllerを生成し、Commentという名前でModelも生成&マイグレーション。

$ rails new commenter-r
$ rails generate controller comments
$ rails generate model Comment content:string
$ bundle exec rake db:migrate

Controllerとルーティングのコードはこんな感じ。

comments_controller.rb
class CommentsController < ApplicationController
  # 全Commentをリスト表示
  def index
    @comment = Comment.new
    @comments = Comment.all
  end

  # コメントをPOST内容から新規作成してindexにリダイレクト
  def create
    Comment.create(content: comment_params[:content])
    redirect_to comments_url
  end

  # コメントを全削除してindexにリダイレクト
  def destroy
    Comment.destroy_all
    redirect_to comments_url
  end

  # Strong Parameters用コード
  
end
routes.rb
Rails.application.routes.draw do
  get 'comments', to: 'comments#index'
  post 'comments', to: 'comments#create'
  delete 'comments', to: 'comments#destroy'

  root 'comments#index'
end

画面は1個なので、Viewはこれだけ。

index.html.erb
<h1>コメントリスト</h1>

<%= form_for(@comment) do |f| %>
  <%= f.text_field :content %>
  <%= f.submit %>
<% end %>

<ui>
  <% @comments.each do |comment| %>
  <li><%= comment.content %></li>
  <% end %>
</ui>

<%= link_to '全て削除', comments_path, method: :delete %>

動作確認。普通はrails sだけど、クラウド上にあるマシンのlocalhostで動かしてもしょうがないので、Cloud9では以下のようなコマンドで起動させます。

$ rails s -p $PORT -b $IP

とりあえずこれで大作アプリ(笑)完成しました。

rails.png

Symfonyで

さあSymfonyでも作るぞ。

PHP…?

そもそもPHPが初めてだったので軽く勉強しました。WikipediaとかQiitaとかの入門記事色々読みました。意外とWikipediaの記事よかったです。

Symfonyのドキュメントも含め、ネット上の記事は古いバージョンのPHP向けの記述も多く、注意が必要です。(現場で使えるPHPのバージョンはまだわからないけど…)Qiitaでは@tsdsanモダンPHPアンチパターン - Qiitaが、過去と現在のPHPの雰囲気をつかめてありがたかったです。

PHPのオレオレ理解。
* javaっぽい見た目
* .の代わりに->
* 変数の頭に$
* ファイルの先頭に<?phpと書くのは鉄の掟。なぜか閉じなくてよい。
* array()は古語。読むのはともかく自分のコードでは[]と書きたい。
* RubyでいうBundler、PHPではComposer

ちょっとSymfony触るだけなので、言語に関してはサラッとすます感じですが、そのうちちゃんと勉強します…

環境

Rails版と同じく、Cloud9で作ることにします。

  • Ubuntu 14.04.3 LTS
  • PHP 5.5.9-1ubuntu4.14 (cli) (built: Oct 28 2015 01:34:46)
  • Symfony version 3.0.3

Symfonyインストール&アプリ新規作成

公式に従い、Symfonyをインストールします。

$ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
$ sudo chmod a+x /usr/local/bin/symfony

そしてcommenter-sプロジェクトを新規作成。

$ symfony new commenter-s

作ったプロジェクトを起動するrails sに相当するコマンドはphp bin/console server:runなんですが、やはりCloud9で動作させるにはちょっとコツが必要。Cloud9の公式にSymfonyプロジェクトの起動方法が載ってます。

raw.png

とりあえず動きました。

バンドル作る

Symfonyで特徴的なのはバンドルという概念です。

Railsでは生成したアプリケーションの雛型にガリガリコードを書いてく感じですが、Symfonyではアプリケーション上にバンドルという再利用可能なモジュールを作り、そこにModel/View/Controllerなどのコードを書きます。Symfonyの本体やプラグインもすべてバンドルで出来ていて、フラクタルな感じがいいですね。

バンドルの命名規約のベストプラクティスも公式にあって、{ベンダー名}/{機能名}Bundleがおすすめらしいです。今回はOreore/CommenterBundleという名前にしました。

$ php bin/console generate:bundle --namespace=Oreore/CommenterBundle

このコマンドでバンドルを生成してくれます。対話的に何やら聞かれますが、今回は全部デフォルトで作りました。

ルーティング

ルーティングは、Railsと同じようにルーティング用の定義ファイルに記述することもできて、日本Symfonyユーザー会のドキュメントではそのようになっています。

でも今イケてるのは、ルーティング先となるControllerのメソッドに、ルーティング元のパスとメソッドをアノテーションで書くスタイルらしいです。

こんな感じ。

src/Oreore/CommenterBundle/Controller/DefaultController.ph
class DefaultController extends Controller
{
    /**
     * @Route("/", name="index")
     * @Method("GET")
     */
    public function indexAction() {}

    /**
     * @Route("/")
     * @Method("POST")
     */
    public function createAction(Request $request) {}

    /**
     * @Route("/destroy", name="destroy")
     */
    public function destroyAction() {}
}

ルートとルーティング先メソッドを並べて見れるため、いい感じだと思います。
ルーティングの一覧は、以下のコマンドで確認できます。

$ php bin/console debug:rou

Model

RailsでActiveRecordに当たるのが、SymfonyではDoctrineというモジュールです。

初めにEntityクラスを作り、これを元にしてDBテーブルを生成できます。

Entityは手書きでもつくれますが、以下のコマンドで対話的にEntityを作成できます。
text型のcontentフィールドを持つ、Commentクラスを作ってみます。

$ php bin/console doctrine:generate:entity
...
# Entity名
The Entity shortcut name: OreoreCommenterBundle:Comment
# カラム名
New field name (press <return> to stop adding fields): content
# カラム型
Field type [string]: text
...

これでcontentフィールドとそのgetter/setterをもつCommentクラスがEntityフォルダに生成されました。各クラスメンバには、ORM用のアノテーションが付加されています。

次のコマンドで、作成済みのEntityクラスからデフォルトのDBエンジン(MySQL)のDBテーブルを生成してくれます。

$ php bin/console doctrine:schema:update --force

Controller

コントローラのindex用メソッドindexActionはこんな感じになりました。

src/Oreore/CommenterBundle/Controller/DefaultController.ph
class DefaultController extends Controller
{
  /**
   * @Route("/", name="index")
   * @Method("GET")
   * @Template()
   */
  public function indexAction()
  {
    // Viewで使うformをセットアップ
    $comment = new Comment();
    $form = $this->createFormBuilder($comment)
      ->add('content', TextType::class)
      ->add('save', SubmitType::class, ['label' => 'Create Comment'])
      ->getForm();

    // DBからコメント一覧取得
    $repository = $this->getDoctrine()
      ->getRepository('OreoreCommenterBundle:Comment');
    $comments = $repository->findAll();

    // Viewに作ったインスタンスを渡す
    return [
      'form' => $form->createView(),
      'comments' => $comments
    ];
  }

基本的にRails版と同じことをやらせてますが、比べると大分コード量増えてますね… ここはまあ設計思想の違いかなと思います。

とにかく短くかけるけど最早Rails語、っていう感じのActiveRecordと違い、Doctrineの場合、ContollerのインスタンスからDoctrine、エンティティをフェッチするためのRepositoryをとってきて、クエリを投げるというインスタンス間の責務が明確です。

それとViewで参照するための変数をreturnしてるのが特徴的かなと思います。テストしやすそうだし、インスタンス変数経由でViewに値を渡すRailsよりも副作用がない感じ。

ちなみにこのようにreturnできるのはアノテーションで@Template()と書いているからで、それがない場合はreturnでレンダリングするHTML文字列を含むResponceインスタンスを返す形になります。それはそれで、HTTPリクエストに対して文字列のレスポンスを返す、というWebアプリケーションの本分が一つのメソッドに集約される形になっていて、割と好きです。

Controllerの残りは、コメント投稿用のcreateActionとコメント全削除用のdestroyActionです。

src/Oreore/CommenterBundle/Controller/DefaultController.ph
class DefaultController extends Controller
{
  /**
   * @Route("/")
   * @Method("POST")
   */
  public function createAction(Request $request)
  {
    // フォームから投稿内容取得
    $data = $request->request->all();
    $content = $data['form']['content'];

    // エンティティインスタンスを作成
    $comment = new Comment();
    $comment->setContent($content);

    // エンティティインスタンスの内容をDBに保存
    $em = $this->getDoctrine()->getManager();
    $em->persist($comment);
    $em->flush();

    // indexにリダイレクト
    return $this->redirectToRoute('index');
  }

  /**
   * @Route("/destroy", name="destroy")
   */
  public function destroyAction()
  {
    // DBから全件取得
    $repository = $this->getDoctrine()
      ->getRepository('OreoreCommenterBundle:Comment');
    $comments = $repository->findAll();

    // 取得内容をDBから全件削除
    $em = $this->getDoctrine()->getManager();
    foreach ($comments as $key => $comment) {
      $em->remove($comment);
    }
    $em->flush();

    // indexにリダイレクト
    return $this->redirectToRoute('index');
  }
}

View & 動作確認

SymfonyのデフォルトのテンプレートエンジンはTwigです。

ERBとかと大体同じで、{{ ... }}で囲うとPHP式の表示、{% ... %}でロジックを書けます。

今回はこんな風になりました。

<h1>コメントリスト</h1>

{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}

<ul>
  {% for comment in comments %}
    <li>{{ comment.content }}</li>
  {% endfor %}
</ul>

<a href="{{ path('destroy') }}">全て削除</a>

form_xxxのところは、Controllerで構築したformをデフォルトの見た目やパラメータでレンダリングしてくれる手続きです。

これで動作確認するとこんな感じになりました。
Rails版とちょっと見た目が違っちゃいましたが、機能的にはほとんど同じです。

symfony.png

感想とか!

  • バンドルという仕組みが面白い。
  • Railsほど、シンプルな記法にこだわってない。
  • アノテーションで出来ることが多い。IDEの恩恵高そう。

参考資料

成果物

41
34
1

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
41
34