Symfony komponenty v Nette

26.01.2019

@xf3l1x
@nettefw

TOC

  1. Symfony Console
  2. Symfony Event Dispatcher
  3. Symfony Serializer
  4. Symfony Validator

Project Nutella

Project Nutella

github.com/planette/nutella-project

Symfony Console

  • command-line interface
  • best PHP console in the 🌎
  • easy to use
  • separation into small classes (commands)
  • colors, styles, printers
  • DIC integration
  • lazy loading
composer require symfony/console

Symfony Console

# bin/console.php
<?php

require __DIR__.'/vendor/autoload.php';

use Symfony\Component\Console\Application;

$application = new Application();
$application->add(new HelloCommand());
$application->run();
class HelloCommand extends Command
{
    protected function configure(): void
    {
        $this->setName('hello');
    }

    protected function execute(InputInterface $input, OutputInterface $output): void
    {
        // magic goes here
    }
}

Symfony Console

# config.neon

services:
	console.application:
		class: Symfony\Component\Console\Application
		setup:
			- setAutoExit(true)
			- setName("Nutella project")
			- setVersion("1.0")

			- add(App\Console\HelloCommand())
#!/usr/bin/env php
<?php

/** @var Nette\DI\Container $container */
$container = require_once __DIR__ . '/../app/bootstrap.php';

// Run application.
$container->getByType(Symfony\Component\Console\Application::class)->run();

Symfony Console

Nutella Project 1.0

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  help                            Displays help for a command
  list                            Lists commands
  hello                           Hello world!

Symfony Console

# config.neon

extensions:
	console: Contributte\Console\DI\ConsoleExtension(%consoleMode%)

console:
	name: Nutella
	version: "1.0"
	url: http://localhost/
	lazy: true

composer require contributte/console

Symfony Console

# config.neon

extensions:
	console: Contributte\Console\DI\ConsoleExtension(%consoleMode%)
	console.extra: Contributte\Console\Extra\DI\ConsoleBridgesExtension

console:
	name: Nutella
	version: "1.0"
	url: http://localhost/
	lazy: true

composer require contributte/console-extra
 nette
  nette:cache:purge               Clear temp folders and others
  nette:caching:clear             Clear Nette Caching Storage
  nette:di:purge                  Clear temp/cache/Nette.Configurator folder
  nette:latte:purge               Clear temp/latte folder
  nette:latte:warmup              Warmup Latte templates (*.latte)
  nette:router:dump               Display all defined routes
  nette:security:password         Generates password (s) using Nette Passwords
  nette:utils:random              Generates random string(s) using Nette Random

Symfony Event Dispatcher

  • business logic separation
  • subscriber / dispatcher
  • easy to use
  • DIC integration
  • lazy loading
composer require symfony/event-dispatcher

Symfony Event Dispatcher

<?php

use Symfony\Component\EventDispatcher\EventDispatcher;

$dispatcher = new EventDispatcher();

$dispatcher->addListener('order.created', function (Event $event) {
    // Hey, we have an order!
});


$dispatcher->addListener('order.created', [$listener, 'onOrderCreated']);
<?php

use Symfony\Component\EventDispatcher\Event;

class OrderListener
{
    public function onOrderCreated(Event $event): void
    {
        // Hey, we have an order!
    }
}

Symfony Event Dispatcher

# config.neon
services:
	events.dispatcher:
		class: Symfony\Component\EventDispatcher\EventDispatcher
		setup:
			- addListener('order.created', App\Model\OrderListener())
use Nette\Application\UI\Form;
use Symfony\Component\EventDispatcher\EventDispatcher;

class OrderPresenter
{
    private $dispatcher;

    public function __construct(EventDispatcher $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function saveOrder(Form $form): void
    {
        // Some logic..
        
        $this->dispatcher->dispatch('order.created', new OrderEvent($order));
    }
}

Symfony Event Dispatcher

# config.neon

extensions:
	events: Contributte\EventDispatcher\DI\EventDispatcherExtension
composer require contributte/event-dispatcher

Symfony Event Dispatcher

<?php declare(strict_types = 1);

namespace App\Model\Order;

use App\Domain\Order\Event\OrderCreated;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Tracy\Debugger;

class OrderLogSubscriber implements EventSubscriberInterface
{
	public static function getSubscribedEvents(): array
	{
		return [
			OrderCreated::NAME => 'onOrderCreated',
		];
	}

	public function onOrderCreated(OrderCreated $event): void
	{
		Debugger::log($event, 'info');
		Debugger::barDump($event);
	}
}

Symfony Event Dispatcher

class OrderLogSubscriber implements EventSubscriberInterface
{
	public static function getSubscribedEvents(): array
	{
		return [
			OrderCreated::NAME => [
				['onOrderCreatedBefore', 100],
				['onOrderCreated', 0],
				['onOrderCreatedAfter', -100],
			],
		];
	}

	public function onOrderCreatedBefore(OrderCreated $event): void
	{
		Debugger::log('BEFORE');
	}

	public function onOrderCreated(OrderCreated $event): void
	{
		Debugger::log($event, 'info');
	}

	public function onOrderCreatedAfter(OrderCreated $event): void
	{
		Debugger::log('AFTER');
	}
}

Live Demo

Time to selfie 📷

Project Forest

{
    "id": 1,
    "username": "felix",
    "email": "felix@nette.org",
    "name": "Felix",
    "surname": "Felicis"
}

Project Forest

github.com/planette/forest-project

Project Forest

Symfony Serializer

  • converts data / objects
  • A ➡️ B  ➡️ A 
  • JSON, XML, YAML, CSV
  • nested structures
  • annotations support
composer require symfony/serializer

😢

Symfony Serializer

Symfony Serializer

$serializer = new Serializer(
    [new PropertyNormalizer()], 
    [new JsonEncoder()]
);


$order = new Order(1);

$json = $serializer->serialize($order, 'json');
// {id: 1}
class Order
{
    private $id;
    
    public function __construct(int $id)
    {
        $this->id = $id;
    }

    public function getId(): int
    {
        return $this->id;
    }
}

Symfony Serializer

$serializer = new Serializer(
    [new PropertyNormalizer()], 
    [new JsonEncoder()]
);


$json = '{"id":1}';

$obj = $serializer->deserialize($json, Order::class, 'json');

Symfony Serializer

# config.neon
services:
    symfony.serializer:
        class: Symfony\Component\Serializer\Serializer(
            [Symfony\Component\Serializer\Normalizer\ObjectNormalizer()],
            [Symfony\Component\Serializer\Encoder\JsonEncoder()]
        )
use Symfony\Component\Serializer\Serializer;

class ApiUserController
{
    private $serializer;

    public function __construct(Serializer $serializer)
    {
        $this->serializer = $serializer;
    }

    public function serialize(User $user)
    {
        $this->serializer->serialize($user, 'json');
    }

    public function deserialize($json)
    {
        $this->serializer->deserialize($json, User::class, 'json');
    }
}

Symfony Serializer

$serializer = new Serializer(
    [new PropertyNormalizer()], 
    [new JsonEncoder()]
);

$user = new User();
$user->addOrder(new Order(1));
$user->addOrder(new Order(2));

$json = $serializer->serialize($order, 'json');
// {"orders":[{"id":1},{"id":2}]}
class Order
{
    private $id;
    
    public function __construct(int $id)
    {
        $this->id = $id;
    }

    public function getId(): int
    {
        return $this->id;
    }
}
class User
{
    /** @var Order[] */
    private $orders = [];

    public function addOrder(Order $order): void
    {
        $this->orders[] = $order;
    }

    public function getOrders(): array
    {
        return $this->orders;
    }
}

Symfony Serializer

$serializer = new Serializer(
    [new PropertyNormalizer()], 
    [new JsonEncoder()]
);

$json = '{"orders":[{"id":1},{"id":2}]}';

$obj = $serializer->deserialize($json, User::class, 'json');

Symfony Serializer

  • ignored_attributes
  • attributes
  • allow_extra_attributes
  • object_to_populate
  • groups (@Groups)
  • skip_null_values
  • default_constructor_arguments
  • callbacks
  • disable_type_enforcement (string,int)
  • enable_max_depth (@MaxDepth)
  • circular_reference_handler
  • circular_reference_limit
$serializer->serialize($obj, 'json', $context);

Symfony Validator

  • validates input / objects
  • cool validator in PHP 🌏
  • many constraints
  • annotation support
composer require symfony/validator

😢

Symfony Validator

use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Validation;

$validator = Validation::createValidator();

$violations = $validator->validate('Felix', [
    new Length(['min' => 10]),
    new NotBlank(),
]);

// This value is too short. 
// It should have 10 characters or more.

Symfony Validator

$user = new UserPojo();
$user->setUsername('Felix');

$validator = Validation::createValidatorBuilder()
	->addMethodMapping('loadValidatorMetadata')
	->getValidator();

$violations = $validator->validate($user);

// This value is too short. 
// It should have 10 characters or more.
class UserPojo
{
    /** @var string */
    private $username;

    // getter/setter

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('username', new NotBlank());
        $metadata->addPropertyConstraint('username', new Length(['min' => 10, 'max' => 20]));
    }
}

Symfony Validator

$user = new User('Felix');

$validator = Validation::createValidatorBuilder()
    ->enableAnnotationMapping()
    ->getValidator();

$violations = $validator->validate($user);

// This value is too short. 
// It should have 10 characters or more.
class User
{
    /**
     * @var string
     * @Assert\NotBlank
     * @Assert\Length(min="10", max="20")
     */
    private $username;

    public function __construct(string $username)
    {
        $this->username = $username;
    }

    // getter
}

Symfony Validator

use Symfony\Component\Validator\Validator\ValidatorInterface;

class ApiUserCreator
{
    private $validator;

    public function __construct(ValidatorInterface $validator)
    {
        $this->validator = $validator;
    }

    public function doMagic()
    {
        $violations = $this->validator->validate($user)
    }
}
// config.neon
services:
    symfony.validator:
        class: Symfony\Component\Validator\Validator\ValidatorInterface
        factory: @symfony.validator.builder::getValidator()
    
    symfony.validator.builder:
        class: Symfony\Component\Validator\ValidatorBuilder
        factory: Symfony\Component\Validator\Validation
            ::createValidatorBuilder()
            ::enableAnnotationMapping()

Symfony Validator

±48 😎

Symfony Validator

class User
{
    /**
     * @Assert\NotBlank(payload={"severity"="error"})
     */
    protected $username;

    /**
     * @Assert\NotBlank(payload={"severity"="error"})
     */
    protected $password;

    /**
     * @Assert\Iban(payload={"severity"="warning"})
     */
    protected $bankAccountNumber;
}

Live Demo

Co Kdyby...

Kdyby\Doctrine

nettrine/orm

Kdyby\Console

contributte/console

Kdyby\Events

contributte/event-dispatcher

contributte/console-extra

nettrine/dbal

contributte/event-dispatcher-extra

Lidi mi

píšou

Akademie42

5.2.2019

Milan Šulc — Vue.js, pomocník nejen s novou single page aplikací

Thank you

enjoy PoSobota

🍻

@xf3l1x

2019-01-26 - Symfony 4.2 komponenty v Nette 2.4

By Milan Felix Šulc

2019-01-26 - Symfony 4.2 komponenty v Nette 2.4

  • 1,703