Днес, когато стартира нов проект, едно от ключовите решения е да се избере правилната рамка. В днешно време е трудно да си представим изграждането на сложно уеб приложение от нулата, без такова.
Много популярни езици за уеб разработка имат своята „стандартна“ рамка, като Ruby on Rails за Ruby или Django за Python. Въпреки това, PHP няма такъв единичен стандарт и има множество популярни опции, от които да избирате.
Според Google тенденции и GitHub , най-популярните PHP рамки са Symfony с 13,7 000 звезди и Laravel с 29 000 звезди (по време на писането на тази статия).
В тази статия ще сравня тези две рамки и ще ви покажа как да внедрите прости, ежедневни функции с всяка. По този начин можете да сравните кода от реални примери един до друг.
Тази статия предполага силни PHP умения и разбиране на архитектурната парадигма на MVC, но не се изисква предишен опит със Symfony или Laravel.
Когато говорим за Laravel, имаме предвид Laravel версия 4 и по-нови. Laravel 4 е издаден през 2013 г. и представлява цялостно пренаписване на рамката. Функционалността на рамката беше разделена на отделни компоненти, които се управляваха с Composer, вместо всичко да е в едно огромно хранилище на кодове.
Laravel се декларира като рамка за бързо развитие с прост и красив синтаксис, който е лесен за научаване, четене и поддържане. Това е най-популярната рамка през 2016 г. Според Google тенденции , той е три пъти по-популярен от други рамки и нататък GitHub , той има два пъти повече звезди от конкурентите.
Symfony 2 беше пуснат през 2011 г., но не трябва да се бърка със Symfony 1, който беше напълно различна рамка с различни основни принципи. Fabien Potencier създаде Symfony 2, а текущата версия е 3.2, което е допълнителна версия на Symfony 2. Следователно те често се наричат просто Symfony2 / 3.
Подобно на Laravel 4, Symfony 2 е проектиран като набор от отделени компоненти. Тук има две предимства: Можем да заменим всеки компонент в проект на Symfony и можем да вземем и използваме всеки компонент на Symfony в проект, който не е на Symfony. Компонентите на Symfony могат да служат като чудесни примери за код и се използват в много от тях проекти с отворен код като Drupal, phpBB и Codeception. Всъщност самият Laravel използва не по-малко от 14 компонента на Symfony. По този начин разбирането на Symfony ви дава много предимства при работа с други проекти.
И двете рамки се предлагат с инсталатори и обвивки, достъпни чрез PHP вграден уеб сървър .
Инсталирането на 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 е достъпна на URL http://localhost:8000
.
Процесът на инсталиране на Laravel е почти същият и толкова прост, колкото този за Symfony; единствената разлика е, че инсталирате инсталатора на Laravel чрез Composer:
# 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 работят по подразбиране от един и същи порт на localhost (8000), така че не можете да изпълнявате тези екземпляри по подразбиране едновременно. Не забравяйте да спрете сървъра на Symfony, като стартирате php bin/console server:stop
преди да стартирате сървъра Laravel.
Това са примери за основна инсталация. За по-усъвършенствани примери за използване, като например възможност за конфигуриране на проекти с локални домейни или стартиране на множество проекти едновременно, и двете рамки предоставят кутии Vagrant:
как да изпратите erc20 токени
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
Инструментът Symfony само за средата за разработка. Този инструмент ви помага да отстранявате грешки и да профилирате приложението си директно в прозореца на браузъра.
Raspberry pi 3 домашен сървър
В конфигурационните файлове можете също да забележите %secret%
конструкции. Те ни позволяват да поставим специфични за околната среда променливи в отделните parameters.yml
файл. Този файл може да бъде уникален за всяка машина и не се съхранява под контрол на версиите. За контрол на версиите имаме специален parameters.yml.dist
файл, който е шаблонът за parameters.yml
файл.
Ето пример за parameters.yml
файл:
parameters: database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null secret: f6b16aea89dc8e4bec811dea7c22d9f0e55593af
Конфигурацията на Laravel изглежда много различно от тази на Symfony. Единственото общо между тях е, че и двамата използват файлове, които не се съхраняват под контрол на версиите (.env
в случая Laravel) и шаблон за генериране на този файл (.env.example
). Този файл има списък с ключове и стойности, като следния пример:
APP_ENV=local APP_KEY=base64:Qm8mIaur5AygPDoOrU+IKecMLWgmcfOjKJItb7Im3Jk= APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost
Подобно на файла Symfony YAML, този за Laravel също е четим от човека и изглежда чист. Можете допълнително да създадете .env.testing
файл, който ще се използва при изпълнение на PHPUnit тестове.
Конфигурацията на приложението се съхранява в .php
файлове в config
директория. Основната конфигурация се съхранява в app.php
файл и специфична за компонента конфигурация се съхранява в .php
файлове (напр. cache.php
или mail.php
). Ето пример за config/app.php
файл:
'Laravel', 'env' => env('APP_ENV', 'production'), 'debug' => env('APP_DEBUG', false), 'url' => env('APP_URL', 'http://localhost'), 'timezone' => 'UTC', 'locale' => 'en', // ... ];
Механизмите за конфигуриране на приложения на Symfony ви позволяват да създавате различни файлове за различни среди. Освен това ви пречи да инжектирате сложна PHP логика в YAML конфигурацията.
Въпреки това може да се чувствате по-удобно със стандартния синтаксис на PHP конфигурацията, който Laravel използва и не е нужно да научавате YAML синтаксис.
По принцип едно уеб приложение има основна отговорност: да прочете всяка заявка и да създаде отговор в зависимост от съдържанието на заявката. Контролерът е клас, отговорен за трансформирането на заявката в отговор чрез извикване на методи на приложение, докато рутерът е механизъм, който ви помага да откриете кой клас и метод на контролера трябва да изпълните за конкретна заявка.
Нека създадем контролер, който ще показва страница на публикация в блог, поискана от /posts/{id}
маршрут.
Контролер
Post::findOrFail($id)]); } }
Рутер
Route::get('/posts/{id}', ' [email protected] ');
Определихме маршрута за GET
заявки. Всички заявки с URI, съответстващи на /posts/{id}
ще изпълни BlogController
controller’s show
метода и ще предаде параметъра id
към този метод. В контролера се опитваме да намерим обекта на модела POST
с преминалия id
и извикайте помощника на Laravel view()
за да изобразите страницата.
В Symfony, exampleController
е малко по-голям:
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.
На този етап може би си мислите, че Laravel е много по-хубав от Symfony. Това е вярно в началото. Изглежда много по-добре и по-лесно да започнете. Въпреки това, в реалните приложения не трябва да извиквате Doctrine от контролера. Вместо това трябва да се обадите на служба, която ще се опита да намери публикацията или да хвърли HTTP 404 Изключение .
Laravel се доставя с шаблонен механизъм, наречен Острие и Symfony се доставя с Клонка . И двата механизма за шаблони изпълняват две основни характеристики:
И двете функции ви позволяват да дефинирате основен шаблон с подменяеми секции и дъщерни шаблони, които запълват стойности на тези секции.
Нека отново разгледаме примера на страница в публикация в блог.
// base.blade.php @section('page-title') Welcome to blog! @show {% block content %}{% endblock %} // 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 %}
Структурно шаблоните Blade и Twig са доста сходни. И двата генерират шаблони в PHP код и работят бързо и и двата изпълняват контролни структури, като if
оператори и цикли. Най-важната характеристика на двата двигателя е, че те избягват изхода по подразбиране, което помага за предотвратяване на XSS атаки.
Освен синтаксиса, основната разлика е, че Blade ви позволява да инжектирате PHP код директно във вашите шаблони, а Twig не. Вместо това Twig ви позволява да използвате филтри.
хакната кредитна карта с баланс 2019
Например, ако искате да заглавите низ, в Blade бихте посочили следното:
{{ ucfirst('welcome friend') }}
В Twig, от друга страна, бихте направили следното:
{capitalize }
В Blade е по-лесно да разширите някои функции, но Twig не позволява директен PHP код в шаблоните.
Приложенията имат много различни услуги и компоненти, с различни взаимозависимости. Трябва да съхранявате по някакъв начин цялата информация за създадените обекти и техните зависимости.
Ето и следващия ни компонент - Контейнер за услуги . Това е PHP обект, който създава заявени услуги и съхранява информация за създадените обекти и техните зависимости.
Нека разгледаме следния пример: Вие създавате клас PostService
да внедрите метод, който е отговорен за създаването на нова публикация в блога. Този клас зависи от две други услуги: PostRepository
, която отговаря за съхраняването на информация в базата данни, и SubscriberNotifier
, която отговаря за уведомяването на абонираните потребители за новата публикация. За да работи, трябва да предадете тези две услуги като аргументи на конструктора на PostService
или, с други думи, трябва да инжектирате тези зависимости.
Първо, нека дефинираме нашите примерни услуги:
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: BlogBundleServicePostService arguments: ['@blog.post_repository', '@blog.subscriber_notifier'] # SubscriberNotifier service. It could also have its own dependencies, for example, mailer class. blog.subscriber_notifier: class: BlogBundleServiceSubscriberNotifier # Repository. Don't dive deep into it's configuration, it is not a subject now blog.post_repository: class: BlogBundleRepositoryPostRepository factory: 'doctrine.orm.default_entity_manager:getRepository' arguments: - BlogBundleEntityPost
Сега можете да поискате вашата пощенска услуга навсякъде в кода от обекта на вашия контейнер за услуги. Например в контролера може да бъде нещо подобно:
// Controller file. $post variable defined below $this->get('blog.post_service')->create($post);
Service Container е чудесен компонент и помага да се изгради приложението ви след ТВЪРДО принципи на проектиране.
Свързани: Инжектиране на истинска зависимост с компоненти на Symfony Пример за инжектиране на зависимост от Laravel
Много по-лесно е да управлявате зависимости в Laravel. Нека разгледаме същия пример:
repository = $repository; $this->notifier = $notifier; } public function create(Post $post) { $this->repository->persist($post); $this->notifier->notifyCreate($post); } }
Тук идва красотата на Laravel - не е необходимо да създавате конфигурации на зависимост . Laravel автоматично сканира зависимостите за PostService
в неговия конструктор типове аргументи и автоматично ги разрешава.
Можете също да използвате инжектиране в метода на вашия контролер, за да използвате PostService
чрез „подсказване на типа“ в аргументите на метода:
'Title', 'content' => 'Content']); $service->create($post); return redirect('/posts/'.$post->id); } }
Инжектиране на зависимост: Symfony срещу Laravel
Автоматичното откриване на Laravel работи чудесно. Symfony има подобна възможност, наречена „ autowire ”, Който е изключен по подразбиране и може да бъде включен чрез добавяне на autowire: true
към вашата конфигурация на зависимостта, но тя изисква известна конфигурация. Начинът Laravel е по-опростен.
Обектно релационно картографиране (ORM)
За работа с бази данни и двете рамки се предлагат с функции обектно-релационно картографиране (ORM). ORM картографира записи от базата данни на обекти в кода. За да направите това, трябва да създадете модели за всеки тип запис (или всяка таблица) във вашата база данни.
Symfony използва проект на трета страна Учение за взаимодействие с базата данни, докато Laravel използва собствена библиотека Красноречив .
Красноречивият ORM изпълнява Модел на ActiveRecord за работа с базата данни. В този модел всеки модел е наясно с връзката с базата данни и може да взаимодейства с нея. Например може да запазва данни в базата данни, да актуализира или изтрива запис.
Доктрината прилага Шаблон за картографиране на данни , където моделите не знаят нищо за базата данни; те са наясно само със самите данни. Специален отделен слой, EntityManager
, съхранява цялата информация за взаимодействието между модели и бази данни и обработва всички операции.
Нека вземем пример, за да разберем разликата. Да приемем, че вашият модел има основна id
ключ, заглавие, съдържание и автор. The Публикации таблица съхранява само автора id
, така че трябва да създадете връзка с Потребители маса.
Учение
Нека започнем с дефиниране на моделите:
Тук създадохме информация за картографиране на модели и вече можем да използваме помощник за генериране на заглушки на метода:
php bin/console doctrine:generate:entities BlogBundle
След това дефинираме методите за следрепозитори:
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);
Красноречив
The Потребител моделът се доставя с Laravel и той е дефиниран по подразбиране, така че трябва да дефинирате само един модел за Публикувай .
разлика между s corp и c corp
belongsTo('AppUser', 'author_id'); } }
Това е всичко за моделите. В Eloquent не е нужно да дефинирате свойствата на модела, тъй като той ги изгражда динамично въз основа на структурата на таблицата на базата данни. За съхраняване на нова публикация $post
в базата данни, трябва да направите това повикване (например от контролера):
$post->save();
За да намерите всички публикации от автор с дадено име, най-добрият подход би бил да намерите потребител с неговото име и да поискате публикациите на всички потребители:
$posts = Post::whereHas('author', function ($q) { $q->where('name', 'Karim'); })->get();
ORM: Symfony срещу Laravel
Що се отнася до ORM, Eloquent изглежда много по-приятелски PHP разработчици и по-лесно за учене от Доктрината.
Диспечер на събития срещу Middleware

Едно от най-важните неща, които трябва да разберете за дадена рамка, е нейният жизнен цикъл.
Symfony и диспечер на събития
За да преобразува заявка в отговор, Symfony използва EventDispatcher. В резултат на това се задействат различни събития от жизнения цикъл и слушатели на специални събития, за да се справят с тези събития. В началото той изпраща kernel.request
събитие, което включва информация за заявка. Основният слушател по подразбиране на това събитие е RouterListener
, който извиква компонента на рутера, за да намери подходящо правило за маршрут за текущата заявка. След това други събития се изпълняват стъпка по стъпка. Типични слушатели на събития са проверка на сигурността, проверка на маркера CSRF и процес на регистриране. Ако искате да добавите някаква функционалност в жизнения цикъл на заявката, трябва да създадете персонализиран EventListener
и го абонирайте за необходимото събитие.
принципи на дефиниране на единството на дизайна
Laravel и Middleware
Laravel използва различно решение: мидълуер. Харесва ми да сравнявам междинен софтуер с лук: Вашето приложение има определени слоеве и заявка преминава през тези слоеве по пътя към контролера и обратно. Така че, ако искате да разширите логиката на приложението си и да добавите някаква функционалност в жизнения цикъл на заявката, трябва да добавите допълнителен слой към списъка си със средна програма и Laravel ще го изпълни.
REST API
Нека се опитаме да създадем основен CRUD пример за управление на публикация в блог:
- Създаване -
POST /posts/
- Прочетете -
GET /posts/{id}
- Актуализация -
PATCH /posts/{id}
- Изтриване -
DELETE /posts/{id}
REST API в Symfony
Symfony няма лесно готово решение за бързо създаване на REST API, но има страхотни пакети от трети страни FOSRestBundle
и JMSSerializerBundle
.
Нека разгледаме минималния работен пример с FOSRestBundle
и JMSSerializerBundle
. След като ги инсталирате и включите в AppKernel
, можете да зададете в конфигурацията на пакета, че ще използвате JSON формат и това не трябва да бъде включено в заявките за URL:
#app/config/config.yml fos_rest: routing_loader: default_format: json include_format: false
В конфигурацията на маршрутизация трябва да посочите, че този контролер ще внедри REST ресурс:
#app/config/routing.yml blog: resource: BlogBundleControllerPostController type: rest
Приложихте метод на персистиране в хранилището в предишния пример; сега трябва да добавите метод за изтриване:
// 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
Ще получите генериран тип формуляр със следния код:
add('title')->add('content'); } /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => 'BlogBundleEntityPost', 'csrf_protection' => false ]); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'post'; } }
Сега нека приложим нашия контролер.
Забележка: кодът, който ще ви покажа, не е перфектен. Той нарушава някои принципи на проектиране, но може лесно да бъде рефакториран. Основната цел е да ви покажа как да приложите всеки метод, стъпка по стъпка.
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.
REST API в Laravel
Първо, трябва да дефинирате маршрути. Можете да направите това в API
раздел от правилата за маршрута, за да изключите някои компоненти по подразбиране на мидълуер и да включите други. API
секция се намира в routes/api.php
файл.
В модела трябва да дефинирате $fillable
свойство да предава променливи в методите за създаване и актуализиране на модела:
Сега нека дефинираме контролера:
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 използвате FosRestBundle
, което обгръща грешки в JSON. В Laravel трябва да го направите сами. Трябва да актуализирате метода на рендиране в манипулатора на изключения, за да върнете JSON грешки за очакване на JSON заявки:
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 има предвидими планове за пускане през следващите шест години, така че е по-малко вероятно да има изненади.