PHPフレームワーク:SymfonyとLaravelのどちらかを選択

公開: 2022-03-11

今日、新しいプロジェクトを開始するときの重要な決定の1つは、適切なフレームワークを選択することです。 今日では、複雑なWebアプリケーションをゼロから構築することを想像するのは難しくなっています。

Web開発で人気のある言語の多くには、RubyのRubyonRailsやPythonのDjangoなどの「デフォルト」フレームワークがあります。 ただし、PHPにはそのような単一のデフォルトはなく、複数の一般的なオプションから選択できます。

GoogleトレンドとGitHubによると、最も人気のあるPHPフレームワークは、星が13.7kのSymfonyと星が29kのLaravelです(この記事の執筆時点)。

この記事では、これら2つのフレームワークを比較し、それぞれにシンプルで日常的な機能を実装する方法を示します。 このようにして、実際の例のコードを並べて比較できます。

この記事は、強力なPHPスキルとMVCアーキテクチャパラダイムの理解を前提としていますが、SymfonyまたはLaravelの経験は必要ありません。

候補者

Laravel

Laravelについて話すときは、Laravelバージョン4以降を指します。 Laravel 4は2013年にリリースされ、フレームワークの完全な書き直しを表しています。 フレームワークの機能は、すべてが1つの巨大なコードリポジトリにあるのではなく、Composerで管理される個別のコンポーネントに分離されました。

Laravelは、習得、読み取り、保守が容易なシンプルで美しい構文を備えた、迅速な開発のためのフレームワークであると宣言しています。 2016年に最も人気のあるフレームワークです。Googleのトレンドによると、他のフレームワークの3倍の人気があり、GitHubでは競合他社の2倍の星があります。

symfony

Symfony 2は2011年にリリースされましたが、Symfony 1と混同しないでください。Symfony1は、基本的な原則が異なるまったく異なるフレームワークでした。 FabienPotencierはSymfony2を作成し、現在のバージョンは3.2です。これはSymfony 2のインクリメンタルバージョンです。したがって、これらは単にSymfony2/3と呼ばれることがよくあります。

Laravel 4と同様に、Symfony2は分離されたコンポーネントのセットとして設計されています。 ここには2つの利点があります。Symfonyプロジェクトの任意のコンポーネントを置き換えることができ、Symfony以外のプロジェクトの任意のSymfonyコンポーネントを取得して使用できます。 symfonyコンポーネントは優れたコード例として役立ち、Drupal、phpBB、Codeceptionなどの多くのオープンソースプロジェクトで使用されています。 実際、Laravel自体は14以上のSymfonyコンポーネントを使用しています。 したがって、Symfonyを理解すると、他のプロジェクトで作業するときに多くの利点が得られます。

フレームワークのインストール

どちらのフレームワークにも、PHP組み込みWebサーバーを介して利用できるインストーラーとラッパーが付属しています。

symfonyのインストール

symfonyのインストールは以下のように簡単です:

 # Downloading Symfony installer sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony # Granting permissions to execute installer sudo chmod a+x /usr/local/bin/symfony # Creating new Symfony project symfony new symfony_project # Launching built-in server cd symfony_project/ && php bin/console server:start

それでおしまい! Symfonyのインストールはhttp://localhost:8000で入手できます。

Laravelのインストール

LaravelのインストールプロセスはSymfonyの場合とほぼ同じで簡単です。 唯一の違いは、Composerを介してLaravelのインストーラーをインストールすることです。

 # Downloading Laravel installer using Composer composer global require "laravel/installer" # Creating new Laravel project laravel new laravel_project # Launching built-in server cd laravel_project/ && php artisan serve

これで、 http://localhost:8000にアクセスして、Laravelのインストールを確認できます。

注: LaravelとSymfonyはどちらもデフォルトで同じローカルホストポート(8000)で実行されるため、これらのデフォルトインスタンスを同時に実行することはできません。 Laravelサーバーを起動する前にphpbin php bin/console server:stopを実行してSymfonyサーバーを停止することを忘れないでください。

フレームワークのインストールについて

これらは基本的なインストールの例です。 ローカルドメインでプロジェクトを構成したり、一度に複数のプロジェクトを実行したりできるなど、より高度な使用例については、両方のフレームワークにVagrantボックスが用意されています。

  • Laravel Homestead、
  • Symfonyホームステッド。

基本的なフレームワーク構成

symfonyの基本構成

symfonyはその設定を指定するための構文としてYAMLを使用します。 デフォルトの構成はapp/config/config.ymlファイルにあり、次の例のようになります。

 imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } framework: secret: '%secret%' router: { resource: '%kernel.root_dir%/config/routing.yml' } # ... # Twig Configuration twig: debug: '%kernel.debug%' strict_variables: '%kernel.debug%' # ...

環境固有の構成を作成するには、基本的な構成パラメーターを含むファイルapp/config/config_ENV.ymlを作成します。 開発環境用のconfig_dev.ymlファイルの例を次に示します。

 imports: - { resource: config.yml } # ... web_profiler: toolbar: true # ...

この例では、開発環境でのみweb_profilerツールをオンにします。 このツールは、ブラウザウィンドウでアプリケーションをデバッグおよびプロファイリングするのに役立ちます。

構成ファイルでは、 %secret%の構造にも気付くことができます。 これにより、環境固有の変数を個別のparameters.ymlファイルに配置できます。 このファイルはすべてのマシンで一意である可能性があり、バージョン管理下に保存されません。 バージョン管理のために、 parameters.ymlファイルのテンプレートである特別なparameters.yml.distファイルがあります。

次に、 parameters.ymlファイルの例を示します。

 parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593af

Laravelの基本構成

Laravelの構成はSymfonyの構成とは大きく異なります。 共通しているのは、バージョン管理下に保存されていないファイル(Laravelの場合は.env )とこのファイルを生成するためのテンプレート( .env.example )の両方を使用していることだけです。 このファイルには、次の例のように、キーと値のリストがあります。

 APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost

Symfony YAMLファイルと同様に、Laravel用のこのファイルも人間が読める形式できれいに見えます。 さらに、PHPUnitテストの実行時に使用される.env.testingファイルを作成できます。

アプリケーション構成は、 configディレクトリの.phpファイルに保存されます。 基本構成はapp.phpファイルに保存され、コンポーネント固有の構成は<component>.phpファイル(例: cache.phpまたはmail.php )に保存されます。 config/app.phpファイルの例を次に示します。

 <?php return [ 'name' => 'Laravel', 'env' => env('APP_ENV', 'production'), 'debug' => env('APP_DEBUG', false), 'url' => env('APP_URL', 'http://localhost'), 'timezone' => 'UTC', 'locale' => 'en', // ... ];

フレームワーク構成:SymfonyとLaravel

Symfonyのアプリケーション構成メカニズムを使用すると、環境ごとに異なるファイルを作成できます。 さらに、YAML構成に複雑なPHPロジックを挿入することを防ぎます。

ただし、Laravelが使用しているデフォルトのPHP構成構文に慣れている場合があり、YAML構文を学ぶ必要はありません。

ルーティングとコントローラー

一般に、バックエンドWebアプリケーションには、各要求を読み取り、要求の内容に応じて応答を作成するという1つの主要な責任があります。 コントローラーは、アプリケーションメソッドを呼び出すことによって要求を応答に変換する役割を担うクラスですが、ルーターは、特定の要求に対して実行する必要があるコントローラークラスとメソッドを検出するのに役立つメカニズムです。

/posts/{id}ルートからリクエストされたブログ投稿ページを表示するコントローラーを作成しましょう。

Laravelのルーティングとコントローラー

コントローラ

<?php namespace App\Http\Controllers; use App\Post; class BlogController extends Controller { /** * Show the blog post * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { return view('post', ['post' => Post::findOrFail($id)]); } }

ルーター

Route::get('/posts/{id}', 'BlogController@show');

GETリクエストのルートを定義しました。 /posts/{id}に一致するURIを持つすべてのリクエストは、 BlogControllerコントローラーのshowメソッドを実行し、パラメーターidをそのメソッドに渡します。 コントローラーでは、渡されたidを持つモデルPOSTのオブジェクトを見つけようとしており、Laravelヘルパーview()を呼び出してページをレンダリングしようとしています。

Symfonyのルーティングとコントローラー

Symfonyでは、 exampleControllerは少し大きくなっています:

 <?php namespace BlogBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class PostController extends Controller { /** * @Route("/posts/{id}") * @param int $id * @return \Symfony\Component\HttpFoundation\Response */ public function indexAction($id) { $repository = $this->getDoctrine()->getRepository('BlogBundle:Post'); $post = $repository->find($id); if ($post === null) { throw $this->createNotFoundException(); } return $this->render('BlogBundle:Post:show.html.twig', ['post'=>$post]); } }

アノテーションに@Route("/posts/{id}”)がすでに含まれていることがわかるので、 routing.yml構成ファイルにコントローラーを含める必要があります。

 blog: resource: "@BlogBundle/Controller/" type: annotation prefix: /

ステップバイステップのロジックは、Laravelの場合と同じです。

ルーティングとコントローラー:SymfonyとLaravel

この段階では、LaravelはSymfonyよりもはるかに優れていると思うかもしれません。 これは最初は真実です。 開始するのがずっと良くて簡単に見えます。 ただし、実際のアプリケーションでは、コントローラーからDoctrineを呼び出さないでください。 代わりに、投稿を検索するか、 HTTP404例外をスローするサービスを呼び出す必要があります。

テンプレート

LaravelにはBladeと呼ばれるテンプレートエンジンが付属しており、SymfonyにはTwigが付属しています。 両方のテンプレートエンジンは、2つの主要な機能を実装しています。

  1. テンプレートの継承
  2. ブロックまたはセクション

どちらの機能でも、オーバーライド可能なセクションを含む基本テンプレートと、それらのセクションの値を埋める子テンプレートを定義できます。

ブログ投稿ページの例をもう一度考えてみましょう。

Laravelブレードテンプレートエンジン

// base.blade.php <html> <head> <style></style> <title>@section('page-title') Welcome to blog! @show </title> </head> <body> <div class="container"> <h1>@yield('title')</h1> <div class="row"> @yield('content') </div> </div> </body> </html> // post.blade.php @extends('base') @section('page-title')Post {{ $post->title }} - read this and more in our blog.@endsection @section('title'){{ $post->title }}@endsection @section('content') {{ $post->content }} @endsection

これで、コントローラーのLaravelにテンプレートpost.blade.phpをレンダリングするように指示できます。 前のControllerの例でview('post', …)呼び出しを覚えていますか? コード内で、他のテンプレートから継承されていることを知る必要はありません。 これはすべて、ビューレベルのテンプレートでのみ定義されます。

SymfonyTwigテンプレートエンジン

// base.html.twig <html> <head> <style></style> <title>{% block page_title %} Welcome to blog! {% endblock %} </title> </head> <body> <div class="container"> <h1>{% block title %}{% endblock %}</h1> <div class="row"> {% block content %}{% endblock %} </div> </div> </body> </html> // show.html.twig {% extends '@Blog/base.html.twig' %} {% block page_title %}Post {{ post.title }} - read this and more in our blog.{% endblock %} {% block title %}{{ post.title }}{% endblock %} {% block content %} {{ post.content }} {% endblock %}

テンプレート:SymfonyとLaravel

構造的に、BladeテンプレートとTw​​igテンプレートは非常に似ています。 どちらもテンプレートをPHPコードに生成して高速に動作し、 ifステートメントやループなどの制御構造を実装します。 両方のエンジンが持つ最も重要な機能は、デフォルトで出力をエスケープすることです。これは、XSS攻撃を防ぐのに役立ちます。

構文は別として、主な違いは、BladeではPHPコードをテンプレートに直接挿入できますが、Twigでは挿入できないことです。 代わりに、Twigではフィルターを使用できます。

たとえば、文字列を大文字にする場合、Bladeで次のように指定します。

 {{ ucfirst('welcome friend') }}

一方、Twigでは、次のようにします。

 {{ 'welcome friend'|capitalize }}

Bladeでは、一部の機能を拡張する方が簡単ですが、Twigではテンプレートに直接PHPコードを含めることはできません。

依存性注入

アプリケーションにはさまざまなサービスとコンポーネントがあり、さまざまな相互依存関係があります。 作成されたオブジェクトとその依存関係に関するすべての情報を何らかの方法で保存する必要があります。

次のコンポーネントであるサービスコンテナが登場します。 これは、要求されたサービスを作成し、作成されたオブジェクトとその依存関係に関する情報を格納するPHPオブジェクトです。

次の例を考えてみましょう。新しいブログ投稿の作成を担当するメソッドを実装するために、クラスPostServiceを作成しています。 このクラスは、他の2つのサービスに依存しています。データベースに情報を格納するPostRepositoryと、サブスクライブされたユーザーに新しい投稿について通知するSubscriberNotifierです。 これを機能させるには、これら2つのサービスをPostServiceのコンストラクター引数として渡す必要があります。つまり、これらの依存関係を挿入する必要があります。

symfonyの依存性注入の例

まず、サンプルサービスを定義しましょう。

 <?php // src/BlogBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { public function persist(Post $post) { // Perform save to db } }
 <?php // src/BlogBundle/Service/SubscriberNotifier.php namespace BlogBundle\Service; use BlogBundle\Entity\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
 <?php // src/BlogBundle/Service/PostService namespace BlogBundle\Service; use BlogBundle\Entity\Post; use BlogBundle\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }

次は、依存性注入の構成です。

 # src/BlogBundle/Resources/config/services.yml services: # Our main service blog.post_service: class: BlogBundle\Service\PostService arguments: ['@blog.post_repository', '@blog.subscriber_notifier'] # SubscriberNotifier service. It could also have its own dependencies, for example, mailer class. blog.subscriber_notifier: class: BlogBundle\Service\SubscriberNotifier # Repository. Don't dive deep into it's configuration, it is not a subject now blog.post_repository: class: BlogBundle\Repository\PostRepository factory: 'doctrine.orm.default_entity_manager:getRepository' arguments: - BlogBundle\Entity\Post

これで、サービスコンテナオブジェクトからコード内のどこにでもポストサービスをリクエストできます。 たとえば、コントローラーでは次のようになります。

 // Controller file. $post variable defined below $this->get('blog.post_service')->create($post);

Service Containerは優れたコンポーネントであり、SOLIDの設計原則に従ってアプリケーションを構築するのに役立ちます。

関連: Symfonyコンポーネントによる真の依存性注入

Laravel依存性注入の例

Laravelで依存関係を管理する方がはるかに簡単です。 同じ例を考えてみましょう:

 <?php // app/Repository/PostRepository.php namespace App\Repository; use App\Post; class PostRepository { public function persist(Post $post) { // Perform save to db } }
 <?php // app/Service/SubscriberNotifier.php namespace App\Service; use App\Post; class SubscriberNotifier { public function notifyCreate(Post $post) { // Notify subscribers } }
 <?php // app/Service/PostService.php namespace App\Service; use App\Post; use App\Repository\PostRepository; class PostService { /** @var PostRepository */ private $repository; /** @var SubscriberNotifier */ private $notifier; public function __construct(PostRepository $repository, SubscriberNotifier $notifier) { $this->repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }

Laravelの美しさはここにあります-依存関係の構成を作成する必要はありません。 Laravelは、コンストラクター引数タイプのPostServiceの依存関係を自動的にスキャンし、それらを自動的に解決します。

コントローラメソッドでインジェクションを使用して、メソッド引数でPostServiceを「タイプヒント」することでPostServiceを使用することもできます。

 <?php namespace App\Http\Controllers; use App\Post; use App\Service\PostService; class BlogController extends Controller { public function create(PostService $service) { $post = new Post(['title' => 'Title', 'content' => 'Content']); $service->create($post); return redirect('/posts/'.$post->id); } }

依存性注入:SymfonyとLaravel

Laravelの自動検出はうまく機能します。 symfonyには「autowire」と呼ばれる同様の機能があり、デフォルトでオフになっており、 autowire: trueますが、いくつかの構成が必要です。 Laravelの方法はもっと簡単です。

オブジェクトリレーショナルマッピング(ORM)

データベースを操作するために、両方のフレームワークにはオブジェクトリレーショナルマッピング(ORM)機能が付属しています。 ORMは、データベースのレコードをコード内のオブジェクトにマップします。 これを行うには、データベース内の各レコードタイプ(または各テーブル)のモデルを作成する必要があります。

SymfonyはサードパーティのプロジェクトDoctrineを使用してデータベースと対話し、Laravelは独自のライブラリEloquentを使用します。

Eloquent ORMは、データベースを操作するためにActiveRecordパターンを実装します。 このパターンでは、各モデルはデータベースへの接続を認識しており、データベースと対話できます。 たとえば、データをデータベースに保存したり、レコードを更新または削除したりできます。

Doctrineはデータマッパーパターンを実装します。モデルはデータベースについて何も知りません。 彼らはデータ自体だけを知っています。 特別な別個のレイヤーであるEntityManagerは、モデルとデータベース間の相互作用に関するすべての情報を格納し、すべての操作を処理します。

違いを理解するために例を見てみましょう。 モデルにプライマリidキー、タイトル、コンテンツ、作成者があるとします。 Postsテーブルには作成者idのみが格納されるため、 Usersテーブルへのリレーションを作成する必要があります。

教義

モデルを定義することから始めましょう:

 <?php // src/BlogBundle/Entity/User.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * User * * @ORM\Table(name="user") * @ORM\Entity */ class User { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=255) */ private $name; }
 <?php // src/BlogBundle/Entity/Post.php namespace BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Post * * @ORM\Table(name="post") * @ORM\Entity(repositoryClass="BlogBundle\Repository\PostRepository") */ class Post { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * * @ORM\Column(name="title", type="string", length=255) */ protected $title; /** * @var string * * @ORM\Column(name="content", type="text") */ protected $content; /** * @var User * * @ORM\ManyToOne(targetEntity="BlogBundle\Entity\User") * @ORM\JoinColumn(name="author_id", referencedColumnName="id") */ protected $author;

ここでは、モデルマッピング情報を作成し、ヘルパーを使用してメソッドスタブを生成できるようになりました。

 php bin/console doctrine:generate:entities BlogBundle

次に、リポジトリ後のメソッドを定義します。

 <?php // src/BlobBundle/Repository/PostRepository.php namespace BlogBundle\Repository; use BlogBundle\Entity\Post; use Doctrine\ORM\EntityRepository; class PostRepository extends EntityRepository { /** * Store post to database * * @param Post $post */ public function persist(Post $post) { $this->getEntityManager()->persist($post); $this->getEntityManager()->flush(); } /** * Search posts with given author's name * * @param string $name * @return array */ public function findByAuthorName($name) { return $this->createQueryBuilder('posts') ->select('posts') ->join('posts.author', 'author') ->where('author.name = :name') ->setParameter('name', $name) ->getQuery() ->getResult(); } }

これで、これらのメソッドをサービスから、またはたとえばPostControllerから呼び出すことができます。

 // To search for posts $posts = $this->getDoctrine()->getRepository('BlogBundle:Post')->findByAuthorName('Karim'); // To save new post in database $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post);

雄弁

UserモデルはLaravelに同梱されており、デフォルトで定義されているため、 Postに対して1つのモデルを定義するだけで済みます。

 <?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { public function author() { return $this->belongsTo('App\User', 'author_id'); } }

モデルは以上です。 Eloquentでは、データベーステーブル構造に基づいて動的にモデルプロパティを構築するため、モデルプロパティを定義する必要はありません。 新しいpost $postをデータベースに保存するには、次の呼び出しを行う必要があります(たとえば、コントローラーから)。

 $post->save();

指定された名前の作成者によるすべての投稿を検索するには、その名前のユーザーを検索し、すべてのユーザーの投稿を要求するのが最善の方法です。

 $posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();

ORM:Symfony vs. Laravel

ORMに関しては、EloquentはPHP開発者にとってはるかに親しみやすく、Doctrineよりも習得しやすいように見えます。

イベントディスパッチャーとミドルウェア

SymfonyとLaravelのライフサイクル

フレームワークについて理解するための最も重要なことの1つは、そのライフサイクルです。

symfonyとイベントディスパッチャー

リクエストをレスポンスに変換するために、SymfonyはEventDispatcherを使用します。 その結果、さまざまなライフサイクルイベントと特別なイベントリスナーを起動して、これらのイベントを処理します。 最初に、リクエスト情報を含むkernel.requestイベントをディスパッチします。 このイベントの主なデフォルトリスナーはRouterListenerであり、ルーターコンポーネントを呼び出して、現在の要求に適したルートルールを見つけます。 この後、他のイベントが段階的に実行されます。 一般的なイベントリスナーは、セキュリティチェック、CSRFトークンの検証、およびロギングプロセスです。 リクエストのライフサイクルにいくつかの機能を追加する場合は、カスタムEventListenerを作成し、それを必要なイベントにサブスクライブする必要があります。

Laravelとミドルウェア

Laravelは別のソリューションを使用しています:ミドルウェア。 私はミドルウェアをタマネギと比較するのが好きです。アプリケーションには特定のレイヤーがあり、リクエストはコントローラーに戻る途中でこれらのレイヤーを通過します。 したがって、アプリケーションロジックを拡張し、リクエストのライフサイクルにいくつかの機能を追加する場合は、ミドルウェアリストにレイヤーを追加する必要があり、Laravelがそれを実行します。

REST API

ブログ投稿を管理するための基本的なCRUDの例を作成してみましょう。

  • 作成POST /posts/
  • 読むGET /posts/{id}
  • 更新- PATCH /posts/{id}
  • 削除- DELETE /posts/{id}

SymfonyのRESTAPI

Symfonyには、REST APIをすばやく作成するための簡単なソリューションはありませんが、優れたサードパーティバンドルであるFOSRestBundleJMSSerializerBundleがあります。

FOSRestBundleJMSSerializerBundleを使用した最小限の作業例を考えてみましょう。 それらをインストールしてAppKernelでオンにした後、JSON形式を使用し、これをURLリクエストに含める必要がないことをバンドル構成で設定できます。

 #app/config/config.yml fos_rest: routing_loader: default_format: json include_format: false

ルーティング構成では、このコントローラーがRESTリソースを実装するように指定する必要があります。

 #app/config/routing.yml blog: resource: BlogBundle\Controller\PostController type: rest

前の例では、リポジトリにpersistメソッドを実装しました。 次に、deleteメソッドを追加する必要があります。

 // src/BlogBundle/Repository/PostRepository.php public function delete(Post $post) { $this->getEntityManager()->remove($post); $this->getEntityManager()->flush(); }

次に、入力要求を受け入れてモデルにマップするためのフォームクラスを作成する必要があります。 CLIヘルパーを使用してこれを行うことができます。

 php bin/console doctrine:generate:form BlogBundle:Post

次のコードで生成されたフォームタイプを受け取ります。

 <?php // src/BlogBundle/Form/PostType.php namespace BlogBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class PostType extends AbstractType { /** * {@inheritdoc} */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('title')->add('content'); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'BlogBundle\Entity\Post', 'csrf_protection' => false ]); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'post'; } }

それでは、コントローラーを実装しましょう。

注:ここで紹介するコードは完全ではありません。 これはいくつかの設計原則に違反していますが、簡単にリファクタリングできます。 主な目的は、各メソッドを実装する方法を段階的に示すことです。

 <?php // src/BlogBundle/Controller/PostController.php namespace BlogBundle\Controller; use BlogBundle\Entity\Post; use BlogBundle\Form\PostType; use FOS\RestBundle\Controller\FOSRestController; use FOS\RestBundle\View\View; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class PostController extends FOSRestController { /** * @param $id * @return Response */ public function getPostAction($id) { $view = new View(); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $view->setData(['post' => $post]); } return $this->handleView($view); } /** * @param Request $request * @return Response */ public function postPostAction(Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = new Post; $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_CREATED); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } return $this->handleView($view); } /** * @param $id * @param Request $request * @return Response */ public function patchPostAction($id, Request $request) { $view = new View(null, Response::HTTP_BAD_REQUEST); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post === null) { $view->setStatusCode(Response::HTTP_NOT_FOUND); } else { $form = $this->createForm(PostType::class, $post, ['method' => $request->getMethod()]); $form->handleRequest($request); if ($form->isValid()) { $this->getDoctrine()->getRepository('BlogBundle:Post')->persist($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); $postUrl = $this->generateUrl('get_post', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL); $view->setHeader('Location', $postUrl); } else { $view->setData($form->getErrors()); } } return $this->handleView($view); } /** * @param $id * @return Response */ public function deletePostAction($id) { $view = new View(null, Response::HTTP_NOT_FOUND); $post = $this->getDoctrine()->getRepository('BlogBundle:Post')->find($id); if ($post !== null) { $this->getDoctrine()->getRepository('BlogBundle:Post')->delete($post); $view->setStatusCode(Response::HTTP_NO_CONTENT); } return $this->handleView($view); } }

FOSRestBundleを使用すると、メソッドごとにルートを宣言する必要はありません。 コントローラのメソッド名の規則に従うだけで、 JMSSerializerBundleはモデルを自動的にJSONに変換します。

LaravelのRESTAPI

まず、ルートを定義する必要があります。 ルートルールのAPIセクションでこれを実行して、一部のデフォルトのミドルウェアコンポーネントをオフにし、他のコンポーネントをオンにすることができます。 APIセクションはroutes/api.phpファイルにあります。

 <?php // routes/api.php Route::resource('/posts', 'BlogController');

モデルでは、モデルのcreateメソッドとupdateメソッドで変数を渡すために$fillableプロパティを定義する必要があります。

 <?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $fillable = ['title', 'content']; // …

次に、コントローラーを定義しましょう。

 <?php // app/Http/Controllers/BlogController.php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Http\Response; class BlogController extends Controller { public function show(Post $post) { return $post; } public function store(Request $request) { $post = Post::create($request->get('post')); return response(null, Response::HTTP_CREATED, ['Location'=>'/posts/'.$post->id]); } public function update(Post $post, Request $request) { $post->update($request->get('post')); return response(null, Response::HTTP_NO_CONTENT, ['Location'=>'/posts/'.$post->id]); } public function destroy(Post $post) { $post->delete(); return response(null, Response::HTTP_NO_CONTENT); } }

Symfonyでは、エラーをJSONでラップするFosRestBundleを使用しています。 Laravelでは、自分で行う必要があります。 JSONリクエストを予期するためのJSONエラーを返すには、Exceptionハンドラーのrenderメソッドを更新する必要があります。

 <?php // app/Exceptions/Handler.php namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; class Handler extends ExceptionHandler { /** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $exception * @return \Illuminate\Http\Response */ public function render($request, Exception $exception) { if ($request->expectsJson()) { $status = 400; if ($this->isHttpException($exception)) { $status = $exception->getStatusCode(); } elseif ($exception instanceof ModelNotFoundException) { $status = 404; } $response = ['message' => $exception->getMessage(), 'code' => $exception->getCode()]; return response()->json($response, $status); } return parent::render($request, $exception); } // ... }

REST API:SymfonyとLaravel

ご覧のとおり、一般的なREST APIの場合、LaravelはSymfonyよりもはるかに単純です。

勝者を選ぶ:SymfonyまたはLaravel?

すべてがあなたの最終目標に依存するので、LaravelとSymfonyの間に明確な勝者はありません。

Laravelは、次の場合に適しています。

  • このフレームワークは、習得が容易で、構文が単純で、教材が優れているため、これが初めての経験です。
  • スタートアップ製品を構築し、仮説を確認しています。これは、迅速なアプリケーション開発に適しており、Laravel開発者は簡単に見つけることができるためです。

Symfonyは、次の場合に最適なオプションです。

  • 非常にスケーラブルで、保守が容易で、適切に構造化されているため、複雑なエンタープライズアプリケーションを構築しています。
  • Symfonyは今後6年間のリリース計画を予測できるため、大規模な長期プロジェクトの移行を構築しているため、予期しない事態が発生する可能性は低くなります。