Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Symfony UX

Symfony UX

Fabien Potencier

December 03, 2020
Tweet

More Decks by Fabien Potencier

Other Decks in Programming

Transcript

  1. Bridging Communities
    Fabien Potencier
    @fabpot

    View full-size slide

  2. Bridging Communities
    Fabien Potencier
    @fabpot

    View full-size slide

  3. 1. The Community


    2. HttpKernelInterface


    3. Symfony Flex

    View full-size slide

  4. A Strong Community


    A growing core team - 17 members


    Amazing support team - many


    Dedicated doc team - 7 members


    A growing community of developers - tons

    View full-size slide

  5. 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);


    }

    View full-size slide

  6. 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

    View full-size slide

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

    View full-size slide

  8. 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

    View full-size slide

  9. 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

    View full-size slide

  10. 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

    View full-size slide

  11. Security


    Bootstrap a new project the fast way
    $ symfony console make:migration


    $ symfony console doctrine:migrations:migrate -n

    View full-size slide

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

    View full-size slide

  13. https://127.0.0.1:8000/logout
    https://127.0.0.1:8000/login

    View full-size slide

  14. 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

    View full-size slide

  15. 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
    ? ?

    View full-size slide

  16. What's next?


    Embrace the larger ecosystem even more

    View full-size slide

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

    View full-size slide

  18. composer req

    symfony/ux-dropzone

    symfony/ux-cropper

    symfony/ux-lazyimg

    symfony/ux-swup

    View full-size slide

  19. 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

    View full-size slide

  20. "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

    View full-size slide

  21. yarn && yarn encore dev

    View full-size slide

  22. {


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

    View full-size slide

  23. +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

    View full-size slide

  24. 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

    View full-size slide

  25. 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

    View full-size slide

  26. 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

    View full-size slide

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

    View full-size slide

  28. 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

    View full-size slide

  29. Noticed how images are loaded?

    View full-size slide

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

    View full-size slide

  31. 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);


    }


    }


    width="200"


    height="150"


    style="visibility: hidden"


    data-blur-hash="{{ blur_hash(photo_dir~'/t_' ~ comment.photofilename


    data-controller="lazyimg"


    data-hd-src="{{ asset('uploads/photos/' ~ comment.photofilename) }}"


    />
    Stimulus


    Controllers
    lazyimg_controller.js
    show.html.twig

    View full-size slide

  32. Develop JS code in a Symfony app


    using plain JS,

    standard JS tools and packages,

    thanks to a few conventions


    and Flex help
    UX

    View full-size slide

  33. The key goal is to enable progressive enhancements


    Standard HTTP


    SEO friendly


    Accessible


    ... and still compatible with VueJS/React/...
    UX

    View full-size slide

  34. Titouan Galopin

    is joining the Symfony Core Team


    Leading the Symfony UX initiative

    View full-size slide

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

    View full-size slide