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

Commander une application Symfony par le texte

Commander une application Symfony par le texte

Quand une application comporte des centaines de fonctionnalités et des millions de lignes en base de données, il est souvent fastidieux d'accéder à une information. Il faut choisir le bon item dans un menu, chercher dans une liste, cliquer sur modifier, accéder à un formulaire pour enfin pouvoir modifier une donnée. Nous allons voir comment on peut facilement ajouter à une application Symfony une UI différente, une interface de commande par texte avec autocompletion.

Richard Hanna

July 17, 2018
Tweet

More Decks by Richard Hanna

Other Decks in Technology

Transcript

  1. @ r i c h a rd h a n

    n a D É V E L O P P E U R
  2. @PodcastEcho Son rôle au sein de Symfony Son parcours et

    projets Opensource Recrutement dans la tech Développeuse au parcours atypique UX mobile et PWA Analyse et 1er bilan de Parcoursup
  3. Interface d'administration d'une application classique. Il y a des listes,

    des boutons, des menus. Il y a des centaines de fonctionnalités et des millions de lignes en base de données. Accéder à une ressource peut parfois être fastidieux : plusieurs clics pour accéder à une fonctionnalité.
  4. use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouterInterface; class AllRoutesResolver { /** @var RouterInterface

    */ private $router; public function __construct(RouterInterface $router) { $this->router = $router; } /** * @return Route[] */ public function getAllRoutes(): array { return $this->router->getRouteCollection()->all(); } } Récupérer toutes les routes de l'application
  5. class RouteGenerator { /** @var RouterInterface */ private $router; public

    function __construct(RouterInterface $router) { $this->router = $router; } public function generateUrl(string $routeName, array $parameters): string { return $this->router->generate($routeName, $parameters); } } Générer l'url
  6. array:140 [▼ 0 => ResultView {#1388 ▼ +label: "User list"

    +routeName: "admin_user_list" +url: "/user/list" } 1 => ResultView {#1403 ▶} 2 => ResultView {#1402 ▶} ... L'ensemble des urls générées
  7. array:140 [▼ 0 => ResultView {#1388 ▼ +label: "User list"

    +routeName: "admin_user_list" +url: "/user/list" +parameters: array:0 [] } 1 => ResultView {#1389 ▼ +label: "User update" +routeName: "admin_user_update" +url: null +parameters: array:1 [▼ "user" => null ] } ⚠ Routes avec des paramètres
  8. User update_ ➡ User update Korben DALLAS ➡ User update

    Leeloo Ekbat De Sebat ➡ User update Cornelius Suggestion de résultats
  9. # Routing admin_user_update: path: /user/update/{user} methods: [GET, POST] requirements: user:

    \d+ defaults: { _controller: AdminBundle:User:update } # Controller class UserController extends Controller { public function updateAction(Request $request, User $user): Response { ou class UpdateUserAction { public function __invoke(Request $request, User $user): Response { # Get Route requirements public function getRequirements(Route $route): array { return array_keys($route->getRequirements()); } Récupérer les requirements d'une route
  10. use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactoryInterface; use Symfony\Component\Routing\Route;

    class RouteGetter { /** ... */ public function __construct( ControllerResolverInterface $controllerResolver, ArgumentMetadataFactoryInterface $argumentMetadataFactory ) { $this->controllerResolver = $controllerResolver; $this->argumentMetadataFactory = $argumentMetadataFactory; } /** @return ArgumentMetadata[] */ public function getArgumentsMetadata(Route $route): array { $request = new Request([], [], ['_controller' => $route->getDefault('_controller')]); $controller = $this->controllerResolver->getController($request); return $this->argumentMetadataFactory->createArgumentMetadata($controller); } } Récupérer les metadata du controller d'une route
  11. array:2 [▼ 0 => ArgumentMetadata {#2146 ▼ -name: "request" -type:

    "Symfony\Component\HttpFoundation\Request" -isVariadic: false -hasDefaultValue: false -defaultValue: null -isNullable: false } 1 => ArgumentMetadata {#2151 ▼ -name: "user" -type: "App\Domain\Model\User" -isVariadic: false -hasDefaultValue: false -defaultValue: null -isNullable: false } ] Récupérer les metadata du controller d'une route
  12. class UserResolver implements ResolverInterface { /** ... */ /** ResultView[]

    */ public function resolve(ResultView $resultView): array { $enabledUsers = $this->userRepository->getEnabledUser(); $resultViews = []; foreach ($enabledUsers as $user) { $resultViews[] = new ResultView($user->getFullName(), ['user' => $user->getId()]); } return $resultViews; } } Custom resolver
  13. array:2 [▼ 0 => ResultView {#1395 ▼ +label: "User update

    Korben DALLAS" +routeName: "admin_user_update" +url: "/user/update/42" +parameters: array:2 [▼ "user" => 42 "_locale" => "en" ] } 1 => ResultView {#1403 ▼ +label: "User update Leeloo Ekbat De Sebat" +routeName: "admin_user_update" +url: "/user/update/1337" +parameters: array:2 [▶] } Custom resolver
  14. use Doctrine\Common\Persistence\ManagerRegistry; class DoctrineResolver { public function __construct(ManagerRegistry $managerRegistry) {

    $this->managerRegistry = $managerRegistry; } public function resolve(ResultView $resultView, string $paramName, string $className): array { $entityManager = $this->managerRegistry->getManagerForClass($className); if (null === $entityManager) { return []; } $objects = $entityManager->getRepository($className)->findAll(); $resultViews = []; foreach ($objects as $object) { $parameters = $resultView->parameters; $parameters[$paramName] = $object->getId(); $resultViews[] = new ResultView( sprintf('%s %s', $resultView->label, $object), $resultView->routeName, $resultView->url, $parameters ); } return $resultViews; } Resolver Doctrine class User { public function __toString(): string { return $this->getDisplayName(); }
  15. # Routing admin_user_list: path: /user/list/{user} admin_user_create: path: /user/create/{user} admin_user_update: path:

    /user/update/{user} # humanized_routes.en.yml admin_user_list: User List admin_user_create: User Create admin_user_update: User Update # humanized_routes.fr.yml admin_user_list: Utilisateur Lister admin_user_create: Utilisateur Créer admin_user_update: Utilisateur Modifier Traduction des noms de route
  16. use Symfony\Component\Translation\TranslatorBagInterface; use Symfony\Component\Translation\TranslatorInterface; class TranslateRouteName { public function __construct(

    TranslatorInterface $translator, TranslatorBagInterface $translatorBag ) { $this->translator = $translator; $this->translatorBag = $translatorBag; } public function handle(string $routeName, string $locale): string { $catalogue = $this->translatorBag->getCatalogue($locale); return $catalogue->has($routeName, 'humanized_routes') ? $this->translator->trans($routeName, [], 'humanized_routes', $locale) : $this->humanizeRouteName($routeName); } protected function humanizeRouteName(string $routeName): string { return ucfirst(str_replace(['admin_', '_'], ['', ' '], $routeName)); } } Traduction des noms de route App\ActionsBot\Resolver\TranslateRouteName: arguments: - '@translator' - '@translator'
  17. ✓ Nouvelle UX / UI basées sur les routes ✓

    Rapidité++ efficacité++ ✓ Commandes auto-générées ou personnalisées ✓ Générer une API à partir du routing ? Bilan
  18. ⤬ inversion langage : "Utilisateur Modifier" au lieu de "Modifier

    utilisateur" ⤬ proposer une recherche en langage naturel ⤬ savoir quoi chercher ⤬ savoir comment chercher Inconvénients
  19. var recognition = new SpeechRecognition(); recognition.continuous = true; recognition.lang =

    'fr-FR'; recognition.onresult = function (event) { for (i = event.resultIndex; i < event.results.length; i++) { var result = event.results[i][0]; console.log(result.transcript + ': ' + result.confidence); } }; recognition.start(); Speech recognition
  20. @ r i c h a rd h a n

    n a D É V E L O P P E U R @PodcastEcho Commander au clavier une application Symfony grâce au Routing!