Comment définir un champ calculé ? À partir de la version 2023.1.2

Un champ calculé est calculé automatiquement en fonction d'autres champs du Smart Element. Il est recalculé dynamiquement à chaque modification dans le formulaire qui pourrait l'impacter.

Recommandation

Si vous n’êtes pas familier avec les champs calculés et leur fonctionnement, il est fortement recommandé de lire la documentation associée.

Prérequis

Afin de pouvoir suivre ce guide, il faut préalablement avoir créé une ou plusieurs Smart Structure(s). Il est également recommandé d'avoir défini des champs étendus.

Prérequis techniques

  • Anakeen Platform 4 - 2023.1.2

Ajouter un champ calculé

Reprenons l'exemple de la Smart Structure MENU. Notre menu se compose d'une liste de PLAT et de BOISSON. Nous avons vu comment calculer le prix total du menu automatiquement. Cependant, ce calcul n'a lieu qu'après une modification du menu et son enregistrement. Nous préférions pouvoir afficher dynamiquement le prix total pendant l'édition.

Définir un champ calculé

Créons une nouvelle Smart Structure COMPUTED_MENU :

npx @anakeen/anakeen-cli createSmartStructure --sourcePath . --name COMPUTED_MENU

Copions le fichier de configuration de la Smart Structure MENU et remplaçons le champ menu_price par un champ calculé :



 

 















 






<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<smart:config xmlns:smart="https://platform.anakeen.com/4/schemas/smart/1.0">
    <smart:structure-configuration name="COMPUTED_MENU" label="Menu dynamique">
        <smart:icon file="menu.png"/>
        <smart:class>Cogip\Restauratec\SmartStructures\ComputedMenu\ComputedMenuBehavior</smart:class>
        <smart:fields>
            <smart:field-set name="menu_frame" type="frame" access="ReadWrite" label="Informations relatives au menu">
                <smart:field-text name="menu_title" needed="true" is-title="true" access="ReadWrite" label="Libellé"/>
                <smart:field-htmltext name="menu_description" access="ReadWrite" label="Description"/>
                <smart:field-set name="menu_composition" type="array" access="ReadWrite" label="Composition du menu">
                    <smart:field-docid access="ReadWrite" name="menu_plat" label="Plat" relation="PLAT"/>
                    <smart:extended-field-money name="menu_plat_price" access="Read" label="Prix du plat" >
                        <smart:copy relation-field="menu_plat" from-field="consommable_price_excl_vat" />
                    </smart:extended-field-money>
                    <smart:field-docid access="ReadWrite" name="menu_boisson" label="Boisson" relation="BOISSON"/>
                    <smart:extended-field-money name="menu_boisson_price" access="Read" label="Prix de la boisson" >
                        <smart:copy relation-field="menu_boisson" from-field="consommable_price_excl_vat" />
                    </smart:extended-field-money>
                </smart:field-set>
                <smart:field-double name="menu_discount" access="ReadWrite" label="Remise applicable (%)"/>
                <smart:computed-field-money name="menu_price" access="Read" label="Prix (€)"/>
            </smart:field-set>
        </smart:fields>
        <smart:defaults/>
    </smart:structure-configuration>
</smart:config>

Informations

Les types de computed-field-<TYPE> disponibles sont les mêmes que ceux des Smart Fields contenant de la donnée, dont la liste est disponible ici, ainsi qu'un type spécial computed-field-array dont le fonctionnement sera détaillé dans le chapitre suivant.

Configurer son calcul

Il faut maintenant définir :

  • quels champs impactent le prix total (ses dépendances)
  • comment calculer le prix total (sa fonction de calcul)

La définition de la fonction de calcul et des dépendances se fait dans une balise computed. Ajoutons la configuration suivante :

























 
 
 
 
 
 
 
 



<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<smart:config xmlns:smart="https://platform.anakeen.com/4/schemas/smart/1.0">
    <smart:structure-configuration name="COMPUTED_MENU" label="Menu dynamique">
        <smart:icon file="menu.png"/>
        <smart:class>Cogip\Restauratec\SmartStructures\ComputedMenu\ComputedMenuBehavior</smart:class>
        <smart:fields>
            <smart:field-set name="menu_frame" type="frame" access="ReadWrite" label="Informations relatives au menu">
                <smart:field-text name="menu_title" needed="true" is-title="true" access="ReadWrite" label="Libellé"/>
                <smart:field-htmltext name="menu_description" access="ReadWrite" label="Description"/>
                <smart:field-set name="menu_composition" type="array" access="ReadWrite" label="Composition du menu">
                    <smart:field-docid access="ReadWrite" name="menu_plat" label="Plat" relation="PLAT"/>
                    <smart:extended-field-money name="menu_plat_price" access="Read" label="Prix du plat" >
                        <smart:copy relation-field="menu_plat" from-field="consommable_price_excl_vat" />
                    </smart:extended-field-money>
                    <smart:field-docid access="ReadWrite" name="menu_boisson" label="Boisson" relation="BOISSON"/>
                    <smart:extended-field-money name="menu_boisson_price" access="Read" label="Prix de la boisson" >
                        <smart:copy relation-field="menu_boisson" from-field="consommable_price_excl_vat" />
                    </smart:extended-field-money>
                </smart:field-set>
                <smart:field-double name="menu_discount" access="ReadWrite" label="Remise applicable (%)"/>
                <smart:computed-field-money name="menu_price" access="Read" label="Prix (€)"/>
            </smart:field-set>
        </smart:fields>
        <smart:defaults/>
        <smart:computed>
            <smart:field-computed field="menu_price">
                <smart:field-callable function="::computeTotalPrice"/>
                <smart:field-argument type="field" name="prix_boissons">menu_boisson_price</smart:field-argument>
                <smart:field-argument type="field" name="prix_plats">menu_plat_price</smart:field-argument>
                <smart:field-argument type="field" name="remise">menu_discount</smart:field-argument>
            </smart:field-computed>
        </smart:computed>
    </smart:structure-configuration>
</smart:config>

Cette configuration indique que le champ calculé menu_price :

  • dépend des valeurs des champs menu_boisson_price, menu_plat_price et menu_discount
  • est calculé via la fonction computeTotalPrice

À retenir

Les field-argument sont les dépendances d'un champ calculé et sont passés en argument de la fonction définie par field-callable/@function.

Information

Il est également possible de passer comme arguments supplémentaires :

  • des propriétés sur le Smart Element (id, initid, fromid, title ou viewId)
<smart:field-argument type="property" name="id">id</smart:field-argument>
  • une chaîne de caractères
<smart:field-argument type="string" name="my_string">Une chaine de caracteres</smart:field-argument>

Rendons-nous dans la classe de comportement de la Smart Structure COMPUTED_MENU est définissons la fonction computeTotalPrice. Comme précisé dans la documentation, cette fonction doit respecter la signature suivante :

function (DynamicUpdateRequest $request, DynamicUpdateResponse $response, $args): DynamicUpdateResponse;

$args est un tableau contenant comme nous l'avons demandé :

  • le prix de toutes les boissons
  • le prix de tous les plats
  • la remise à appliquer

Définissons notre fonction de calcul du prix total :










 
 
 
 
 
 
 
 
 
 
 
 


<?php

namespace Cogip\Restauratec\SmartStructures\ComputedMenu;

use Anakeen\Core\SmartStructure\Dynamic\DynamicUpdateRequest;
use Anakeen\Core\SmartStructure\Dynamic\DynamicUpdateResponse;

class ComputedMenuBehavior extends \Anakeen\SmartElement
{
  public function computeTotalPrice(DynamicUpdateRequest $request, DynamicUpdateResponse $response, $args): DynamicUpdateResponse
  {
    $total = 0;
    $consommablesPrice = array_merge($args['prix_boissons'], $args['prix_plats']);
    foreach ($consommablesPrice as $price) {
      if ($price !== null) {
        $total += $price;
      }
    }
    $total = $total * (1.0 - (floatval($args['remise'] ?? 0) / 100.0));
    return $response->setResult($total);
  }
}

Informations

L'attribut field-argument/@name est optionnel, la valeur du champ est alors ajoutée dans $args sans clé et dans l'ordre de définition des arguments.

Attention

Pensez à déployer vos modifications.

make deploy

Résultat

Rendons-nous maintenant sur le formulaire de création d'un COMPUTED_MENU.

À chaque ajout ou modification de plat/boisson ou à chaque changement sur le taux de remise, le champ total est modifié et mis en évidence :

Mise à jour dynamique;

Informations

Vous pouvez également noter que le champ étendu du prix du consommable est également mis à jour de manière dynamique.

Notre menu affiche désormais un prix dynamique 🎉 !

Cas particulier des tableaux

Un champ calculé peut évidemment être intégré dans un tableau. S'il dépend d'autres champs du tableau, il aura à sa disposition, pour son calcul, uniquement la valeur de sa dépendance au même index. Cette dépendance est dite inline.

Ajoutons dans notre exemple un champ calculant la somme du prix du plat et de la boisson par entrée dans le tableau de la composition du menu :



















 













 
 
 
 
 




<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<smart:config xmlns:smart="https://platform.anakeen.com/4/schemas/smart/1.0">
    <smart:structure-configuration name="COMPUTED_MENU" label="Menu dynamique">
        <smart:icon file="menu.png"/>
        <smart:class>Cogip\Restauratec\SmartStructures\ComputedMenu\ComputedMenuBehavior</smart:class>
        <smart:fields>
            <smart:field-set name="menu_frame" type="frame" access="ReadWrite" label="Informations relatives au menu">
                <smart:field-text name="menu_title" needed="true" is-title="true" access="ReadWrite" label="Libellé"/>
                <smart:field-htmltext name="menu_description" access="ReadWrite" label="Description"/>
                <smart:field-set name="menu_composition" type="array" access="ReadWrite" label="Composition du menu">
                    <smart:field-docid access="ReadWrite" name="menu_plat" label="Plat" relation="PLAT"/>
                    <smart:extended-field-money name="menu_plat_price" access="Read" label="Prix du plat" >
                        <smart:copy relation-field="menu_plat" from-field="consommable_price_excl_vat" />
                    </smart:extended-field-money>
                    <smart:field-docid access="ReadWrite" name="menu_boisson" label="Boisson" relation="BOISSON"/>
                    <smart:extended-field-money name="menu_boisson_price" access="Read" label="Prix de la boisson" >
                        <smart:copy relation-field="menu_boisson" from-field="consommable_price_excl_vat" />
                    </smart:extended-field-money>
                    <smart:computed-field-money name="combined_price" access="Read" label="Somme"/>
                </smart:field-set>
                <smart:field-double name="menu_discount" access="ReadWrite" label="Remise applicable (%)"/>
                <smart:computed-field-money name="menu_price" access="Read" label="Prix (€)"/>
            </smart:field-set>
        </smart:fields>
        <smart:defaults/>
        <smart:computed>
            <smart:field-computed field="menu_price">
                <smart:field-callable function="::computeTotalPrice"/>
                <smart:field-argument type="field" name="prix_boissons">menu_boisson_price</smart:field-argument>
                <smart:field-argument type="field" name="prix_plats">menu_plat_price</smart:field-argument>
                <smart:field-argument type="field" name="remise">menu_discount</smart:field-argument>
            </smart:field-computed>
            <smart:field-computed field="combined_price">
                <smart:field-callable function="::computeCombinedPrice"/>
                <smart:field-argument type="field">menu_boisson_price</smart:field-argument>
                <smart:field-argument type="field">menu_plat_price</smart:field-argument>
            </smart:field-computed>
        </smart:computed>
    </smart:structure-configuration>
</smart:config>

Définissons maintenant la fonction réalisant la somme des deux prix :

<?php

namespace Cogip\Restauratec\SmartStructures\ComputedMenu;

use Anakeen\Core\SmartStructure\Dynamic\DynamicUpdateRequest;
use Anakeen\Core\SmartStructure\Dynamic\DynamicUpdateResponse;

class ComputedMenuBehavior extends \Anakeen\SmartElement
{
  public function computeTotalPrice(DynamicUpdateRequest $request, DynamicUpdateResponse $response, $args): DynamicUpdateResponse
  {
    $total = 0;
    $consommablesPrice = array_merge($args['prix_boissons'], $args['prix_plats']);
    foreach ($consommablesPrice as $price) {
      if ($price !== null) {
        $total += $price;
      }
    }
    $total = $total * (1.0 - (floatval($args['remise'] ?? 0) / 100.0));
    return $response->setResult($total);
  }

  public function computeCombinedPrice(DynamicUpdateRequest $request, DynamicUpdateResponse $response, $args): DynamicUpdateResponse
  {
    return $response->setResult(($args[0] ?? 0) + ($args[1] ?? 0));
  }
}

$args[0] et $args[1] sont respectivement le prix de la boisson et le prix du plat à l'index concerné par la modification. Leur nom d'argument n'ayant pas été spécifié, ils sont fournis sans clé et dans l'ordre fourni par la configuration.

Redéployons nos changements :

make deploy

Rendons-nous de nouveau sur le formulaire de création de COMPUTED_MENU :

Mise à jour dynamique inline

La somme est bien calculée dynamiquement à chaque modification du plat ou du de la boisson sélectionnée, et ce, uniquement sur la ligne concernée.

Et ensuite ?

Continuons sur les champs calculés en nous intéressant aux tableaux calculés.