PHP-Frameworks: Die Wahl zwischen Symfony und Laravel

Veröffentlicht: 2022-03-11

Wenn Sie heute ein neues Projekt starten, ist es eine der wichtigsten Entscheidungen, das richtige Framework auszuwählen. Es ist heutzutage schwer vorstellbar, eine komplexe Webanwendung ohne eine solche von Grund auf neu zu erstellen.

Viele gängige Sprachen für die Webentwicklung haben ihr „Standard“-Framework, wie Ruby on Rails für Ruby oder Django für Python. PHP hat jedoch keinen solchen einzigen Standard und hat mehrere beliebte Optionen zur Auswahl.

Laut Google Trends und GitHub sind die beliebtesten PHP-Frameworks Symfony mit 13,7.000 Sternen und Laravel mit 29.000 Sternen (zum Zeitpunkt der Erstellung dieses Artikels).

In diesem Artikel werde ich diese beiden Frameworks vergleichen und Ihnen zeigen, wie Sie mit beiden einfache, alltägliche Funktionen implementieren können. Auf diese Weise können Sie den Code von realen Beispielen nebeneinander vergleichen.

Dieser Artikel setzt starke PHP-Kenntnisse und ein Verständnis des MVC-Architekturparadigmas voraus, es sind jedoch keine Vorkenntnisse mit Symfony oder Laravel erforderlich.

Die Anwärter

Laravel

Wenn wir über Laravel sprechen, beziehen wir uns auf Laravel Version 4 und höher. Laravel 4 wurde 2013 veröffentlicht und stellte eine komplette Neufassung des Frameworks dar. Die Funktionalität des Frameworks wurde in separate Komponenten entkoppelt, die mit Composer verwaltet wurden, anstatt alles in einem einzigen riesigen Code-Repository zu haben.

Laravel erklärt sich selbst als Framework für schnelle Entwicklung mit einer einfachen und schönen Syntax, die leicht zu erlernen, zu lesen und zu warten ist. Es ist das beliebteste Framework im Jahr 2016. Laut Google-Trends ist es dreimal beliebter als andere Frameworks und auf GitHub hat es zweimal mehr Sterne als die Konkurrenz.

Symfonie

Symfony 2 wurde 2011 veröffentlicht, darf aber nicht mit Symfony 1 verwechselt werden, das ein völlig anderes Framework mit anderen zugrunde liegenden Prinzipien war. Fabien Potencier hat Symfony 2 erstellt, und die aktuelle Version ist 3.2, eine inkrementelle Version von Symfony 2. Daher werden sie oft einfach Symfony2/3 genannt.

Symfony 2 ist wie Laravel 4 als ein Satz entkoppelter Komponenten konzipiert. Hier gibt es zwei Vorteile: Wir können jede Komponente in einem Symfony-Projekt ersetzen, und wir können jede Symfony-Komponente in einem Nicht-Symfony-Projekt übernehmen und verwenden. Symfony-Komponenten können als großartige Codebeispiele dienen und werden in vielen Open-Source-Projekten wie Drupal, phpBB und Codeception verwendet. Tatsächlich verwendet Laravel selbst nicht weniger als 14 Symfony-Komponenten. Das Verständnis von Symfony bietet Ihnen daher viele Vorteile bei der Arbeit mit anderen Projekten.

Rahmeninstallationen

Beide Frameworks werden mit Installern und Wrappern geliefert, die über den in PHP integrierten Webserver verfügbar sind.

Symfony-Installation

Die Installation von Symfony ist so einfach wie folgt:

 # 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

Das ist es! Ihre Symfony-Installation ist unter der URL http://localhost:8000 verfügbar.

Laravel-Installation

Der Laravel-Installationsprozess ist fast derselbe und so einfach wie der für Symfony; Der einzige Unterschied besteht darin, dass Sie das Installationsprogramm von Laravel über Composer installieren:

 # 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

Sie können jetzt http://localhost:8000 besuchen und Ihre Laravel-Installation überprüfen.

Hinweis: Sowohl Laravel als auch Symfony laufen standardmäßig über denselben Localhost-Port (8000), sodass Sie diese Standardinstanzen nicht gleichzeitig ausführen können. Vergessen Sie nicht, den Symfony-Server zu stoppen, indem Sie php bin/console server:stop ausführen, bevor Sie den Laravel-Server starten.

Informationen zur Framework-Installation

Dies sind Beispiele für eine Basisinstallation. Für fortgeschrittenere Verwendungsbeispiele, z. B. die Möglichkeit, Projekte mit lokalen Domänen zu konfigurieren oder mehrere Projekte gleichzeitig auszuführen, bieten beide Frameworks Vagrant-Boxen:

  • Laravel-Gehöft,
  • Symfony-Gehöft.

Grundlegende Framework-Konfigurationen

Symfony-Basiskonfiguration

Symfony verwendet YAML als Syntax zur Angabe seiner Konfiguration. Die Standardkonfiguration befindet sich in der Datei app/config/config.yml und sieht wie im folgenden Beispiel aus:

 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%' # ...

Um eine umgebungsspezifische Konfiguration zu erstellen, erstellen Sie die Datei app/config/config_ENV.yml , die die grundlegenden Konfigurationsparameter enthält. Hier ist ein Beispiel für eine config_dev.yml -Datei für die Entwicklungsumgebung:

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

Dieses Beispiel aktiviert das Symfony-Tool web_profiler nur für die Entwicklungsumgebung. Dieses Tool hilft Ihnen, Ihre Anwendung direkt im Browserfenster zu debuggen und zu profilieren.

In den Konfigurationsdateien können Sie auch %secret% Konstruktionen erkennen. Sie ermöglichen es uns, umgebungsspezifische Variablen in die separate Datei parameters.yml aufzunehmen. Diese Datei könnte auf jedem Computer einzigartig sein und wird nicht unter Versionskontrolle gespeichert. Für die Versionskontrolle haben wir eine spezielle Datei parameters.yml.dist , die die Vorlage für die Datei parameters.yml ist.

Hier ist ein Beispiel für die Datei parameters.yml :

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

Laravel-Basiskonfiguration

Die Laravel-Konfiguration sieht ganz anders aus als die von Symfony. Die einzige Gemeinsamkeit besteht darin, dass beide Dateien verwenden, die nicht unter Versionskontrolle gespeichert sind ( .env im Laravel-Fall) und eine Vorlage zum Generieren dieser Datei ( .env.example ). Diese Datei enthält eine Liste mit Schlüsseln und Werten, wie im folgenden Beispiel:

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

Wie die Symfony-YAML-Datei ist auch diese für Laravel für Menschen lesbar und sieht sauber aus. Sie können zusätzlich eine .env.testing -Datei erstellen, die beim Ausführen von PHPUnit-Tests verwendet wird.

Die Anwendungskonfiguration wird in .php Dateien im Verzeichnis config gespeichert. Die grundlegende Konfiguration wird in der Datei app.php gespeichert, und die komponentenspezifische Konfiguration wird in den Dateien <component>.php (z. B. cache.php oder mail.php ) gespeichert. Hier ist ein Beispiel für eine config/app.php -Datei:

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

Framework-Konfiguration: Symfony vs. Laravel

Mit den Anwendungskonfigurationsmechanismen von Symfony können Sie verschiedene Dateien für verschiedene Umgebungen erstellen. Außerdem verhindert es, dass Sie komplexe PHP-Logik in die YAML-Konfiguration einfügen.

Möglicherweise fühlen Sie sich jedoch mit der standardmäßigen PHP-Konfigurationssyntax, die Laravel verwendet, wohler und müssen die YAML-Syntax nicht lernen.

Routing und Controller

Im Allgemeinen hat eine Back-End-Webanwendung eine Hauptaufgabe: jede Anfrage zu lesen und eine Antwort abhängig vom Inhalt der Anfrage zu erstellen. Der Controller ist eine Klasse, die dafür verantwortlich ist, die Anforderung durch Aufrufen von Anwendungsmethoden in die Antwort umzuwandeln, während der Router ein Mechanismus ist, der Ihnen hilft, zu erkennen, welche Controller-Klasse und -Methode Sie für eine bestimmte Anforderung ausführen sollten.

Lassen Sie uns einen Controller erstellen, der eine Blog-Post-Seite anzeigt, die von der Route /posts/{id} angefordert wird.

Routing und Controller in Laravel

Regler

 <?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)]); } }

Router

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

Wir haben die Route für GET Anfragen definiert. Alle Anforderungen mit dem URI, der mit /posts/{id} übereinstimmt, führen die show -the-Methode des BlogController -Controllers aus und übergeben die Parameter- id an diese Methode. Im Controller versuchen wir, das Objekt des Modells POST mit der übergebenen id zu finden, und rufen Laravel helper view() auf, um die Seite zu rendern.

Routing und Controller in Symfony

In Symfony ist exampleController etwas größer:

 <?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]); } }

Sie können sehen, dass wir @Route("/posts/{id}”) bereits in die Anmerkung eingefügt haben, also müssen wir nur den Controller in die Konfigurationsdatei routing.yml :

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

Die Schritt-für-Schritt-Logik ist die gleiche wie im Laravel-Fall.

Routing und Controller: Symfony vs. Laravel

Zu diesem Zeitpunkt denken Sie vielleicht, dass Laravel viel netter ist als Symfony. Das stimmt am Anfang. Es sieht viel besser aus und ist einfacher zu starten. In realen Anwendungen sollten Sie Doctrine jedoch nicht vom Controller aus aufrufen. Stattdessen sollten Sie einen Dienst aufrufen, der versucht, den Post zu finden, oder HTTP 404 Exception auslösen .

Vorlagen

Laravel wird mit einer Template-Engine namens Blade und Symfony mit Twig ausgeliefert. Beide Template-Engines implementieren zwei Hauptfunktionen:

  1. Vorlagenvererbung
  2. Blöcke oder Abschnitte

Mit beiden Funktionen können Sie eine Basisvorlage mit überschreibbaren Abschnitten und untergeordneten Vorlagen definieren, die die Werte dieser Abschnitte füllen.

Betrachten wir noch einmal das Beispiel einer Blogbeitragsseite.

Laravel-Blade-Template-Engine

 // 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

Jetzt können Sie Laravel in Ihrem Controller anweisen, die Vorlage post.blade.php zu rendern. Erinnern Sie sich an unseren view('post', …) -Aufruf im vorherigen Controller-Beispiel? Sie müssen in Ihrem Code nicht wissen, dass er von einer anderen Vorlage geerbt wird. Es wird alles nur in Ihren Vorlagen auf Ansichtsebene definiert.

Symfony Twig Template-Engine

 // 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 %}

Vorlagen: Symfony vs. Laravel

Strukturell sind Blade- und Twig-Templates ziemlich ähnlich. Beide generieren Vorlagen in PHP-Code und arbeiten schnell, und beide implementieren Kontrollstrukturen wie if -Anweisungen und Schleifen. Das wichtigste Merkmal beider Engines ist, dass sie die Ausgabe standardmäßig maskieren, wodurch XSS-Angriffe verhindert werden.

Abgesehen von der Syntax besteht der Hauptunterschied darin, dass Sie mit Blade PHP-Code direkt in Ihre Vorlagen einfügen können und Twig dies nicht tut. Stattdessen ermöglicht Twig die Verwendung von Filtern.

Wenn Sie beispielsweise eine Zeichenfolge groß schreiben möchten, geben Sie in Blade Folgendes an:

 {{ ucfirst('welcome friend') }}

In Twig hingegen würden Sie Folgendes tun:

 {{ 'welcome friend'|capitalize }}

In Blade ist es einfacher, einige Funktionen zu erweitern, aber Twig erlaubt keinen direkten PHP-Code in Vorlagen.

Abhängigkeitsspritze

Anwendungen haben viele verschiedene Dienste und Komponenten mit verschiedenen gegenseitigen Abhängigkeiten. Sie müssen alle Informationen über die erstellten Objekte und ihre Abhängigkeiten irgendwie speichern.

Hier kommt unsere nächste Komponente – Service Container. Es ist ein PHP-Objekt, das angeforderte Dienste erstellt und Informationen über die erstellten Objekte und ihre Abhängigkeiten speichert.

Betrachten wir das folgende Beispiel: Sie erstellen eine Klasse PostService , um eine Methode zu implementieren, die für die Erstellung eines neuen Blogbeitrags verantwortlich ist. Diese Klasse hängt von zwei anderen Diensten ab: PostRepository , das für das Speichern von Informationen in der Datenbank verantwortlich ist, und SubscriberNotifier , das dafür verantwortlich ist, abonnierte Benutzer über den neuen Beitrag zu benachrichtigen. Damit es funktioniert, müssen Sie diese beiden Dienste als Konstruktorargumente von PostService oder, mit anderen Worten, Sie müssen diese Abhängigkeiten einfügen.

Symfony Dependency Injection Beispiel

Lassen Sie uns zunächst unsere Beispieldienste definieren:

 <?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); } }

Als nächstes folgt die Abhängigkeitsinjektionskonfiguration:

 # 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

Jetzt können Sie Ihren Post-Service überall im Code von Ihrem Service Container-Objekt anfordern. Im Controller könnte das z.B. so aussehen:

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

Service Container ist eine großartige Komponente und hilft Ihnen, Ihre Anwendung nach SOLID-Designprinzipien zu erstellen.

Verwandt: True Dependency Injection mit Symfony-Komponenten

Beispiel für Laravel-Abhängigkeitsinjektion

Es ist viel einfacher, Abhängigkeiten in Laravel zu verwalten. Betrachten wir dasselbe Beispiel:

 <?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); } }

Hier kommt das Schöne an Laravel – Sie müssen keine Abhängigkeitskonfigurationen erstellen . Laravel scannt automatisch die Abhängigkeiten für PostService in seinen Konstruktorargumenttypen und löst sie automatisch auf.

Sie können auch die Injektion in Ihrer Controller-Methode verwenden, um PostService zu verwenden, indem Sie es in Methodenargumenten „typisieren“:

 <?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); } }

Abhängigkeitsinjektion: Symfony vs. Laravel

Die automatische Erkennung von Laravel funktioniert hervorragend. Symfony hat eine ähnliche Funktion namens „autowire“, die standardmäßig deaktiviert ist und durch Hinzufügen von autowire: true Ihrer Abhängigkeitskonfiguration, erfordert jedoch eine gewisse Konfiguration. Der Laravel-Weg ist einfacher.

Objektrelationale Zuordnung (ORM)

Um mit Datenbanken zu arbeiten, verfügen beide Frameworks über Object-Relational Mapping (ORM)-Funktionen. ORM ordnet Datensätze aus der Datenbank Objekten im Code zu. Dazu müssen Sie Modelle für jeden Datensatztyp (oder jede Tabelle) in Ihrer Datenbank erstellen.

Symfony verwendet ein Drittanbieterprojekt Doctrine, um mit der Datenbank zu interagieren, während Laravel seine eigene Bibliothek Eloquent verwendet.

Das Eloquent ORM implementiert das ActiveRecord-Muster, um mit der Datenbank zu arbeiten. In diesem Muster ist sich jedes Modell der Verbindung zur Datenbank bewusst und kann damit interagieren. Beispielsweise kann es Daten in der Datenbank speichern, einen Datensatz aktualisieren oder löschen.

Doctrine implementiert das Data Mapper-Muster, bei dem Modelle nichts über die Datenbank wissen; sie kennen nur die Daten selbst. Eine spezielle separate Schicht, EntityManager , speichert alle Informationen über die Interaktion zwischen Modellen und Datenbanken und verarbeitet alle Operationen.

Nehmen wir ein Beispiel, um den Unterschied zu verstehen. Angenommen, Ihr Modell hat einen primären id -Schlüssel, Titel, Inhalt und Autor. Die Posts -Tabelle speichert nur die Autoren- id , daher müssen Sie eine Beziehung zur Users -Tabelle erstellen.

Lehre

Beginnen wir mit der Definition der Modelle:

 <?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;

Hier haben wir Modell-Mapping-Informationen erstellt und können nun einen Helfer verwenden, um die Methoden-Stubs zu generieren:

 php bin/console doctrine:generate:entities BlogBundle

Als nächstes definieren wir Post-Repository-Methoden:

 <?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(); } }

Jetzt können Sie diese Methoden aus dem Dienst oder beispielsweise aus 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);

Beredt

Das Benutzermodell wird mit Laravel geliefert und ist standardmäßig definiert, sodass Sie nur ein Modell für die Post definieren müssen.

 <?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'); } }

Das ist alles für Modelle. In Eloquent müssen Sie keine Modelleigenschaften definieren, da es sie dynamisch basierend auf der Datenbanktabellenstruktur erstellt. Um einen neuen Beitrag $post in der Datenbank zu speichern, müssen Sie diesen Aufruf ausführen (z. B. vom Controller):

 $post->save();

Um alle Beiträge eines Autors mit einem bestimmten Namen zu finden, wäre der beste Ansatz, einen Benutzer mit seinem Namen zu finden und die Beiträge aller Benutzer anzufordern:

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

ORM: Symfony vs. Laravel

In Bezug auf ORM sieht Eloquent viel freundlicher für PHP-Entwickler und einfacher zu erlernen aus als Doctrine.

Event-Dispatcher vs. Middleware

Symfony vs. Laravel-Lebenszyklus

Eines der wichtigsten Dinge, die man über ein Framework verstehen muss, ist sein Lebenszyklus.

Symfony und Event-Dispatcher

Um eine Anfrage in eine Antwort umzuwandeln, verwendet Symfony EventDispatcher. Folglich werden verschiedene Lebenszyklusereignisse und spezielle Ereignis-Listener ausgelöst, um diese Ereignisse zu verarbeiten. Am Anfang sendet es das Ereignis kernel.request , das Anforderungsinformationen enthält. Der wichtigste Standard-Listener dieses Ereignisses ist RouterListener , der die Router-Komponente aufruft, um eine geeignete Routing-Regel für die aktuelle Anforderung zu finden. Danach werden schrittweise weitere Ereignisse ausgeführt. Typische Ereignis-Listener sind eine Sicherheitsprüfung, eine CSRF-Token-Verifizierung und ein Protokollierungsprozess. Wenn Sie dem Anforderungslebenszyklus einige Funktionen hinzufügen möchten, müssen Sie einen benutzerdefinierten EventListener erstellen und ihn für das erforderliche Ereignis abonnieren.

Laravel und Middleware

Laravel verwendet eine andere Lösung: Middleware. Ich vergleiche Middleware gerne mit einer Zwiebel: Ihre Anwendung hat bestimmte Schichten und eine Anfrage durchläuft diese Schichten auf dem Weg zum Controller und zurück. Wenn Sie also Ihre Anwendungslogik erweitern und einige Funktionen im Anforderungslebenszyklus hinzufügen möchten, müssen Sie Ihrer Middleware-Liste eine zusätzliche Ebene hinzufügen, und Laravel wird sie ausführen.

REST-API

Versuchen wir, ein einfaches CRUD-Beispiel zu erstellen, um einen Blogbeitrag zu verwalten:

  • Erstellen - POST /posts/
  • Lesen - GET /posts/{id}
  • Update - PATCH /posts/{id}
  • Löschen - DELETE /posts/{id}

REST-API in Symfony

Symfony hat keine einfache Out-of-the-Box-Lösung für die schnelle REST-API-Erstellung, aber es hat großartige Drittanbieter-Bundles FOSRestBundle und JMSSerializerBundle .

Betrachten wir das minimal funktionierende Beispiel mit FOSRestBundle und JMSSerializerBundle . Nachdem Sie sie installiert und in AppKernel haben, können Sie in der Bundle-Konfiguration festlegen, dass Sie das JSON-Format verwenden und dies nicht in den URL-Anforderungen enthalten sein muss:

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

In der Routing-Konfiguration sollten Sie angeben, dass dieser Controller eine REST-Ressource implementiert:

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

Sie haben im vorherigen Beispiel eine persistente Methode im Repository implementiert; Jetzt müssen Sie eine Löschmethode hinzufügen:

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

Als Nächstes müssen Sie eine Formularklasse erstellen, um Eingabeanforderungen zu akzeptieren und sie dem Modell zuzuordnen. Sie können dies mit einem CLI-Hilfsprogramm tun:

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

Sie erhalten einen generierten Formulartyp mit folgendem Code:

 <?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'; } }

Lassen Sie uns nun unseren Controller implementieren.

Hinweis: Der Code, den ich Ihnen zeigen werde, ist nicht perfekt. Es verstößt gegen einige Designprinzipien, könnte aber leicht umgestaltet werden. Der Hauptzweck besteht darin, Ihnen Schritt für Schritt zu zeigen, wie Sie jede Methode implementieren.

 <?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); } }

Mit FOSRestBundle müssen Sie nicht für jede Methode eine Route deklarieren; Folgen Sie einfach der Konvention mit Controller-Methodennamen, und JMSSerializerBundle konvertiert Ihre Modelle automatisch in JSON.

REST-API in Laravel

Zuerst müssen Sie Routen definieren. Sie können dies im API Abschnitt der Routingregeln tun, um einige Standard-Middleware-Komponenten zu deaktivieren und andere zu aktivieren. Der API -Abschnitt befindet sich in der Datei routes/api.php .

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

Im Modell sollten Sie die $fillable Eigenschaft definieren, um Variablen in den Erstellungs- und Aktualisierungsmethoden des Modells zu übergeben:

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

Lassen Sie uns nun den Controller definieren:

 <?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); } }

In Symfony verwenden Sie FosRestBundle , das Fehler in JSON verpackt. In Laravel müssen Sie es selbst tun. Sie müssen die render-Methode im Exception-Handler aktualisieren, um JSON-Fehler für das Erwarten von JSON-Anforderungen zurückzugeben:

 <?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 vs. Laravel

Wie Sie sehen können, ist Laravel für eine typische REST-API viel einfacher als Symfony.

Auswahl des Gewinners: Symfony oder Laravel?

Es gibt keinen klaren Gewinner zwischen Laravel und Symfony, da alles von Ihrem endgültigen Ziel abhängt.

Laravel ist eine bessere Wahl, wenn:

  • Dies ist Ihre erste Erfahrung mit dem Framework, da es leicht zu erlernen ist und über eine einfachere Syntax und bessere Lernmaterialien verfügt.
  • Sie bauen ein Startup-Produkt und überprüfen Ihre Hypothese, da es gut für die schnelle Anwendungsentwicklung ist und Laravel-Entwickler leicht zu finden sind.

Symfony ist die beste Option, wenn:

  • Sie erstellen eine komplexe Unternehmensanwendung, da sie sehr skalierbar, wartbar und gut strukturiert ist.
  • Sie bauen eine Migration eines großen langfristigen Projekts auf, da Symfony vorhersehbare Veröffentlichungspläne für die nächsten sechs Jahre hat, sodass es weniger wahrscheinlich ist, dass es irgendwelche Überraschungen gibt.