Slide 1

Slide 1 text

Bridging Communities Fabien Potencier @fabpot

Slide 2

Slide 2 text

Bridging Communities Fabien Potencier @fabpot

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

1. The Community 2. HttpKernelInterface 3. Symfony Flex

Slide 5

Slide 5 text

A Strong Community A growing core team - 17 members Amazing support team - many Dedicated doc team - 7 members A growing community of developers - tons

Slide 6

Slide 6 text

HttpKernelInterface has not changed since 2.0 Last change was before 2.0 10 years ago Simple interfaces are powerful Standards are powerful BC policy is important namespace Symfony\Component\HttpKernel; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; interface HttpKernelInterface { const MASTER_REQUEST = 1; const SUB_REQUEST = 2; /** * Handles a Request to convert it to a Response. */ public function handle(Request $request, int $type = self::MASTER_REQUEST, bool $catch = true); }

Slide 7

Slide 7 text

Flex enables Components to act as a Framework Standalone Components (think PEAR reinvented) FrameworkBundle as a set of default con f i gurations for core features Flex enables any package to feel like a Symfony core feature

Slide 8

Slide 8 text

RAD?

Slide 9

Slide 9 text

Security Bootstrap a new project the fast way $ symfony new demo $ composer req maker profiler orm validator form security $ vi docker-compose.yml $ docker-compose up -d $ symfony server:start -d $ symfony open:local version: '3' services: database: image: postgres:12-alpine environment: POSTGRES_USER: main POSTGRES_PASSWORD: main POSTGRES_DB: main ports: [5432] mailer: image: schickling/mailcatcher ports: [1025, 1080] Symfony CLI Docker Compose Composer & Flex

Slide 10

Slide 10 text

The name of the security user class (e.g. User) [User]: > Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]: > Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]: > Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server). Does this app need to hash/check user passwords? (yes/no) [yes]: > $ symfony console make:user Maker bundle generates code that you own

Slide 11

Slide 11 text

What style of authentication do you want? [Empty authenticator]: [0] Empty authenticator [1] Login form authenticator > 1 The class name of the authenticator to create (e.g. AppCustomAuthenticator): > AppAuthenticator Choose a name for the controller class (e.g. SecurityController) [SecurityController]: > Do you want to generate a '/logout' URL? (yes/no) [yes]: > $ symfony console make:auth

Slide 12

Slide 12 text

Creating a registration form for App\Entity\User Do you want to add a @UniqueEntity validation annotation on your User class to make sure duplicate accounts aren't created? (yes/no) [yes]: > Do you want to send an email to verify the user's email address after registration? (yes/no) [yes]: > [WARNING] We're missing some important components. Don't forget to install these after you're finished. composer require symfonycasts/verify-email-bundle symfony/mailer What email address will be used to send registration confirmations? e.g. [email protected]: > [email protected] What "name" should be associated with that email address? e.g. "Acme Mail Bot": > Demo Bot Do you want to automatically authenticate the user after registration? (yes/no) [yes]: > $ symfony console make:registration-form

Slide 13

Slide 13 text

Security Bootstrap a new project the fast way $ symfony console make:migration $ symfony console doctrine:migrations:migrate -n

Slide 14

Slide 14 text

https://127.0.0.1:8000/register $ symfony open:local:webmail

Slide 15

Slide 15 text

https://127.0.0.1:8000/logout https://127.0.0.1:8000/login

Slide 16

Slide 16 text

Security Symfony 5.2+ features Passwordless login link authentication 
 https://symfony.com/doc/5.2/security/login_link.html 2FA/MFA 
 https://github.com/scheb/2fa Help needed - https://github.com/symfony/symfony/issues/30914 Sudo mode and more

Slide 17

Slide 17 text

Symfony CLI Docker Compose Symfony Flex RAD thanks to a tight integration 
 with the larger ecosystem Maker Bundle Docker Composer PHP Packages Webpack Webpack Encore The Magic Glue ? ?

Slide 18

Slide 18 text

What's next? Embrace the larger ecosystem even more

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

http://prototypejs.org/ - https://symfony.com/legacy/doc/tutorial/1_0/en/symfony-ajax

Slide 21

Slide 21 text

??

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

UX

Slide 26

Slide 26 text

composer req 
 symfony/ux-dropzone 
 symfony/ux-cropper 
 symfony/ux-lazyimg 
 symfony/ux-swup

Slide 27

Slide 27 text

Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], + Symfony\UX\Cropper\SymfonyCropperBundle::class => ['all' => true], + Symfony\UX\Dropzone\SymfonyDropzoneBundle::class => ['all' => true], + Symfony\UX\Lazyimg\SymfonyLazyimgBundle::class => ['all' => true], ]; config/bundles.php

Slide 28

Slide 28 text

"popper.js": "^1.16.1", "regenerator-runtime": "^0.13.2", "sass-loader": "^7.0.1", - "webpack-notifier": "^1.6.0" + "webpack-notifier": "^1.6.0", + "@symfony/ux-cropper": "file:vendor/symfony/ux-cropper/assets", + "@symfony/ux-dropzone": "file:vendor/symfony/ux-dropzone/assets", + "@symfony/ux-lazyimg": "file:vendor/symfony/ux-lazyimg/assets", + "@symfony/ux-swup": "file:vendor/symfony/ux-swup/assets" }, "license": "UNLICENSED", "private": true, package.json

Slide 29

Slide 29 text

yarn && yarn encore dev

Slide 30

Slide 30 text

{ - "controllers": {}, - "autoimport": {}, "entrypoints": { "app": "app.js" + }, + "controllers": { + "@symfony/ux-cropper": { + "cropper": { "enabled": true, "webpackMode": "eager" } + }, + "@symfony/ux-dropzone": { + "dropzone": { "enabled": true, "webpackMode": "eager" } + }, + "@symfony/ux-lazyimg": { + "lazyimg": { "enabled": true, "webpackMode": "eager" } + }, + "@symfony/ux-swup": { + "swup": { "enabled": true, "webpackMode": "eager" } + } + }, + "autoimport": { + "@symfony/ux-cropper": { + "dist/style.js": { "enabled": true } + }, + "@symfony\/ux-dropzone": { + "dist/style.css": { "enabled": true } + } } assets/controllers.json

Slide 31

Slide 31 text

+import { startStimulusApp } from '@symfony/stimulus-bridge'; +import '@symfony/autoimport'; + +export const app = startStimulusApp(require.context('./controllers', true, /\.(j|t)sx?$/)); assets/bootstrap.js // only needed for CDN's or sub-directory deploy //.setManifestKeyPrefix('build/') + // enables Symfony - Stimulus bridge + .enableStimulusBridge({ + entrypoints: './assets/controllers.json', + lockFile: './symfony.lock', + }) // When enabled, Webpack "splits" your files into smaller pieces for greater optimization. .splitEntryChunks() webpack.config.js +{ + "controllers": {}, + "autoimport": {}, + "entrypoints": { + "app": "app.js" + } +} assets/controllers.json

Slide 32

Slide 32 text

composer req 
 symfony/ux-dropzone 
 symfony/ux-cropper 
 symfony/ux-lazyimg 
 symfony/ux-swup yarn && yarn encore dev Flex & Encore ❤ ❤ UX Backend World Frontend World

Slide 33

Slide 33 text

DropZone use App\Entity\Comment; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\EmailType; -use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Image; +use Symfony\UX\Dropzone\Form\DropzoneType; class CommentFormType extends AbstractType { @@ -21,7 +21,7 @@ class CommentFormType extends AbstractType ]) ->add('text') ->add('email', EmailType::class) - ->add('photo', FileType::class, [ + ->add('photo', DropzoneType::class, [ 'required' => false, 'mapped' => false, 'constraints' => [ CommentFormType.php

Slide 34

Slide 34 text

Cropper use Symfony\UX\Cropper\Cropper; use Symfony\UX\Cropper\Form\CropperType; class ConferenceController extends AbstractController { /** * @Route("/{_locale<%app.supported_locales%>}/comment/{id}/crop", name="comment_crop") */ public function crop(Request $request, Comment $comment, Cropper $cropper, string $photoDir) { // Redirect to the conference page if no photo // Otherwise, allow the user to crop the uploaded image $crop = $cropper->createCrop($photoDir.'/'.$comment->getPhotoFilename()); $crop->setCroppedMaxSize(2000, 1500); $form = $this->createFormBuilder(['crop' => $crop]) ->add('crop', CropperType::class, [ 'public_url' => '/uploads/photos/'.$comment->getPhotoFilename(), 'aspect_ratio' => 2000 / 1500, ]) ->add('submit', SubmitType::class) ->getForm() ; // Handle the form } } {% block body %}

Crop your photo

{{ form(form) }}
{% endblock %} cropper.html.twig ConferenceController.php

Slide 35

Slide 35 text

Like a Native App? AJAX requests Browser History "Static" area Transition between "pages"

Slide 36

Slide 36 text

Swup diff --git a/templates/base.html.twig b/templates/base.html.twig index 604ffa7..e97dbf6 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -11,7 +11,7 @@ {{ encore_entry_link_tags('app') }} {% endblock %} - +

Conference Guestbook @@ -56,7 +56,7 @@

- + {% block body %}{% endblock %} diff --git a/templates/conference/crop.html.twig b/templates/conference/crop.html.twig index bc6a89d..55f4e91 100644 --- a/templates/conference/crop.html.twig +++ b/templates/conference/crop.html.twig @@ -15,7 +15,7 @@ Crop your photo - {{ form(form) }} + {{ form(form, {'attr': {'data-swup-form': '1'}}) }} diff --git a/templates/conference/show.html.twig b/templates/conference/show.html.twig index 4cbe628..20f37cd 100644 --- a/templates/conference/show.html.twig +++ b/templates/conference/show.html.twig @@ -67,7 +67,7 @@ Add your own feedback - {{ form(comment_form) }} + {{ form(comment_form, {'attr': {'data-swup-form': '1'}}) }} show.html.twig crop.html.twig base.html.twig

Slide 37

Slide 37 text

Noticed how images are loaded?

Slide 38

Slide 38 text

BlurHash is a compact representation of a placeholder for an image LEHV6nWB2yk8pyoJadR*.7kCMdnj https://blurha.sh/ BlurHash?

Slide 39

Slide 39 text

import { Controller } from 'stimulus'; import { decode } from 'blurhash'; export default class extends Controller { connect() { // If blur-hash is defined, use it if (this.element.hasAttribute('data-blur-hash') && !this.element.src) { const pixels = decode(this.element.getAttribute('data-blur-hash'), this.element.width, this.element.height); const canvas = document.createElement('canvas'); canvas.width = this.element.width; canvas.height = this.element.height; const ctx = canvas.getContext('2d'); const imageData = ctx.createImageData(this.element.width, this.element.height); imageData.data.set(pixels); ctx.putImageData(imageData, 0, 0); this.element.src = canvas.toDataURL(); } this.element.style.visibility = 'initial'; // Load HD in background const hd = new Image(); // Once loaded, replace image hd.addEventListener('load', () => { this.element.src = this.element.getAttribute('data-hd-src'); }); setTimeout(() => hd.src = this.element.getAttribute('data-hd-src'), 2000); } } Stimulus Controllers lazyimg_controller.js show.html.twig

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

Develop JS code in a Symfony app using plain JS, 
 standard JS tools and packages, 
 thanks to a few conventions and Flex help UX

Slide 42

Slide 42 text

The key goal is to enable progressive enhancements Standard HTTP SEO friendly Accessible ... and still compatible with VueJS/React/... UX

Slide 43

Slide 43 text

Titouan Galopin 
 is joining the Symfony Core Team Leading the Symfony UX initiative

Slide 44

Slide 44 text

https://symfony.com/sponsor Sponsor Symfony Thank you!