"Après-midi balançoire" - 1978

"Après-midi balançoire" - 1980

Une histoire de

compréhension

AFUP Day Lyon 2023

@matarld
@afup
@florianm__

client

@matarld
@afup
@florianm__

customer

acquéreur

utilisateur

patient

majeur

client

@matarld
@afup
@florianm__

user

élève

utilisateur

client

@matarld
@afup
@florianm__

Florian Merle

@florianm__
Florian-Merle
AKAWAKA

ancien élève

@matarld
@afup
@florianm__

ancien élève aussi

@matarld
mtarld
les-tilleuls.coop

Mathias Arlaud

@matarld
@afup
@florianm__

user

élève

utilisateur

client

@matarld
@afup
@florianm__

élève

$user
@matarld
@afup
@florianm__
$student

élève

@matarld
@afup
@florianm__
$eleve

élève

@matarld
@afup
@florianm__

L'élève est éligible pour une bourse

@matarld
@afup
@florianm__

L'éleve a eu une bourse en 2020 et est inscrit à Parcours Sup et a été accepté à un de ses voeux se trouvant loin de son domicile

@matarld
@afup
@florianm__
$eleve->bourses
  ->filter(/* lycée 2020 */)
  ->count() > 0

&& $parcoursSup->inscrits
  ->contains($eleve)

&& $eleve->voeux
  ->filter(/* accepté */)
  ->filter(/* loin */)
  ->count() > 0;

L'éleve a eu une bourse en 2020 et est inscrit à Parcours Sup et a été accepté à un de ses voeux se trouvant loin de son domicile

@matarld
@afup
@florianm__
$parcoursSup->inscrits
  ->contains($eleve)

&& $eleve->voeux
  ->filter(/* accepté */)
  ->filter(/* loin */)
  ->count() > 0

|| $potDeVin > UnBonMontant->value;

L'éleve est inscrit à Parcours Sup et a été accepté à un de ses voeux se trouvant loin de son domicile ou a versé un bon pot de vin

@matarld
@afup
@florianm__

Expression language

@matarld
@afup
@florianm__

Expression language

@matarld
@afup
@florianm__
$services->set('indexBuilder')
  ->arg(
    '$indexName',
    expr("env('index_prefix') ~ '_' ~ parameter('main_index_name')"),
  );
@matarld
@afup
@florianm__
use Symfony\Component\Security\Http\Attribute\IsGranted;

final class ViewPostController
{
  #[IsGranted('"ROLE_ADMIN" in roles or is_granted("VIEW", subject)', subject: 'post')]
  public function __invoke(Post $post): Response
  {
    return new Response('ok');
  }
}
@matarld
@afup
@florianm__

Expression language

@matarld
@afup
@florianm__
$el = new ExpressionLanguage();

$somme = $el->evaluate('a + b', [
  'a' => 1,
  'b' => 2,
]);
@matarld
@afup
@florianm__
$el = new ExpressionLanguage();

$redouble = $el->evaluate(
  '((notes.semestre * 2 + notes.soutenance) / 3) < 10',
  ['notes' => $eleve->getNotes()]),
);
@matarld
@afup
@florianm__
$eleve->bourses
  ->filter(/* lycée 2020 */)
  ->count() > 0

&& $parcoursSup->inscrits
  ->contains($eleve)

&& $eleve->voeux
  ->filter(/* accepté */)
  ->filter(/* loin */)
  ->count() > 0;

L'éleve a eu une bourse en 2020 et est inscrit à Parcours Sup et a été accepté à un de ses voeux se trouvant loin de son domicile

@matarld
@afup
@florianm__
$el = new ExpressionLanguage();

$reglePourEtreEligible = <<<EOT
  a_eu_une_bourse(eleve, 2020)
  && est_inscrit_a_parcours_sup(eleve)
  && a_voeux_accepté_et_loin(eleve)'
EOT;

$eligible = $el->evaluate(
  $reglePourEtreEligible,
  ['eleve' => $eleve],
);

L'éleve a eu une bourse en 2020 et est inscrit à Parcours Sup et a été accepté à un de ses voeux se trouvant loin de son domicile

@matarld
@afup
@florianm__
$el = new ExpressionLanguage();

$eligible = $el->evaluate(
  $regles->findEligiblePourBourse(),
  ['eleve' => $eleve],
);

L'éleve a eu une bourse en 2020 et est inscrit à Parcours Sup et a été accepté à un de ses voeux se trouvant loin de son domicile

@matarld
@afup
@florianm__
@matarld
@afup
@florianm__

L'arbre syntaxique

@matarld
@afup
@florianm__

L'AFUP organise des trucs cools

@matarld
@afup
@florianm__

L'AFUP organise des trucs cools

VERBE

NOM

DET

DET

NOM

ADJ

@matarld
@afup
@florianm__

L'AFUP organise des trucs cools

VERBE

NOM

DET

DET

NOM

ADJ

GRP NOMINAL

VERBE

GRP NOMINAL

@matarld
@afup
@florianm__

L'AFUP organise des trucs cools

VERBE

NOM

DET

DET

NOM

ADJ

GRP NOMINAL

VERBE

GRP NOMINAL

GRP NOMINAL

GRP VERBAL

@matarld
@afup
@florianm__

L'AFUP organise des trucs cools

VERBE

NOM

DET

DET

NOM

ADJ

GRP NOMINAL

VERBE

GRP NOMINAL

GRP NOMINAL

GRP VERBAL

PHRASE

@matarld
@afup
@florianm__

L'AFUP organise des trucs cools

VERBE

NOM

DET

DET

NOM

ADJ

GRP NOMINAL

VERBE

GRP NOMINAL

GRP NOMINAL

GRP VERBAL

PHRASE

P
GN
GV
D
N
V
GN
D
N
A
@matarld
@afup
@florianm__
(soutenance + notes.semestre) / 2
@matarld
@afup
@florianm__
(soutenance + notes.semestre) / 2

VAR.

OP.

(

VAR.

)

OP.

CST.

.

CST.

@matarld
@afup
@florianm__
(soutenance + notes.semestre) / 2

VAR.

OP.

(

VAR.

)

OP.

CST.

.

CST.

VAR.

OP.

(

PROPERTY ACCESSOR

)

OP.

CST.

@matarld
@afup
@florianm__
(soutenance + notes.semestre) / 2

VAR.

OP.

(

VAR.

)

OP.

CST.

.

CST.

VAR.

OP.

(

PROPERTY ACCESSOR

)

OP.

CST.

ADDITION OPERATOR

OP.

CST.

@matarld
@afup
@florianm__
(soutenance + notes.semestre) / 2

VAR.

OP.

(

VAR.

)

OP.

CST.

.

CST.

VAR.

OP.

(

PROPERTY ACCESSOR

)

OP.

CST.

ADDITION OPERATOR

OP.

CST.

DIVISION OPERATOR

@matarld
@afup
@florianm__
(soutenance + notes.semestre) / 2

VAR.

OP.

(

VAR.

)

OP.

CST.

.

CST.

VAR.

OP.

(

PROPERTY ACCESSOR

)

OP.

CST.

ADDITION OPERATOR

OP.

CST.

DIVISION OPERATOR

/
+
C
V
P
V
C
@matarld
@afup
@florianm__

Lexer

foo === bar.baz
// Chaine de caractères
@matarld
@afup
@florianm__

Lexer

foo === bar.baz
f
o
o
=
=
=
b
a
r
.
b
a
z
// Chaine de caractères
// Flux de caractères
_
_
@matarld
@afup
@florianm__

Lexer

foo === bar.baz
f
o
o
=
=
=
b
a
r
.
b
a
z
foo
===
bar
.
baz
// Chaine de caractères
// Flux de caractères
// Tokens

VAR.

OP.

VAR.

.

CST.

@matarld
@afup
@florianm__

Parser

foo
===
bar
.
baz

PROPERTY ACCESSOR

VAR.

OP.

VAR.

CST.

.

VAR.

OP.

IDENTICAL OPERATOR

===
V
P
V
C
// Tokens
@matarld
@afup
@florianm__

Expression Language

===
V
P
V
C
// Arbre syntaxique
// Magie
@matarld
@afup
@florianm__
eleve in parcoursSup.inscrits
@matarld
@afup
@florianm__

syntaxe spécifique

structure interne

in
.inscrits
eleve in parcoursSup.inscrits
@matarld
@afup
@florianm__
est_inscrit_a_parcours_sup(eleve)
$parcoursSup->inscrits->contains($eleve)
@matarld
@afup
@florianm__
new ExpressionFunction(

  name: 'est_inscrit_a_parcours_sup',
  
  compiler: static function (string $s): string {
    // ...
  },

  evaluator: static function (array $args, Eleve $e): bool {
    return $args['parcoursSup']->inscrits->contains($e);
  },

)
@matarld
@afup
@florianm__
final class ParcoursSupFunctionProvider implements ExpressionFunctionProviderInterface
{
    public function getFunctions(): array
    {
        return [
            new ExpressionFunction('est_inscrit_a_parcours_sup', ...),
            new ExpressionFunction(...),
            // ...
        ];
    }
}
@matarld
@afup
@florianm__
use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseEL;

final class EcoleExpressionLanguage extends BaseEL
{
    public function __construct(
      CacheItemPoolInterface $cache = null,
      array $providers = [],
    ) {
        array_unshift($providers, new ParcoursSupFunctionProvider());
        array_unshift($providers, new BourseFunctionProvider());

        parent::__construct($cache, $providers);
    }
}
@matarld
@afup
@florianm__
$el = new EcoleExpressionLanguage();

$reglePourEtreEligible = <<<EOT
  a_eu_une_bourse(eleve, 2020)
  && est_inscrit_a_parcours_sup(eleve)
  && a_voeux_accepté_et_loin(eleve)'
EOT;

$eligible = $el->evaluate($reglePourEtreEligible, [
  'eleve' => $eleve,
  'parcoursSup' => $parcoursSup,
]);
@matarld
@afup
@florianm__

Démo'

@matarld
@afup
@florianm__
final class DefinirRegleEligiblePourBourseAction extends AbstractController
{
  public function __invoke(EcoleExpressionLanguage $el, RegleRepository $regles)
  {
    if ($form->isSubmitted() && $form->isValid()) {
      $regles->saveEligiblePourBourse($form->get('expression')->getData());
      
      return $this->redirectToRoute('regles');
    }

    return $this->render('definir_regle_eligible_pour_bourse.html.twig', [
      'completion' => [
        ...array_map(
          fn ($f) => ['type' => 'function', 'label' => $f->getName()],
          $el->getFunctions(),
        ),
        ...array_map(
          fn ($v) => ['type' => 'variable', 'label' => $v],
          ['eleve', 'parcoursSup'],
        ),
      ],
    ]);
  }
}
@matarld
@afup
@florianm__
readonly final class AppliqueBourseCommandHandler
{
  public function __construct(
    private EcoleExpressionLanguage $el,
    private RegleRepository $regles,
    private ParcoursSup $parcoursSup,
  ) {
  }

  public function __invoke(AppliqueBourseCommand $command): void
  {
    $eligible = $el->evaluate($regles->findEligiblePourBourse(), [
      'eleve' => $command->eleve,
      'parcoursSup' => $this->parcoursSup,
    ]);
    
    if (!$eligible) {
      return;
    }
    
    // ...
  }
}
@matarld
@afup
@florianm__
@matarld
@afup
@florianm__

Merci !

vos retours