Outils pour utilisateurs

Outils du site


Panneau latéral

ref:blocs:cas_d_utilisation

Cas d'utilisation

Ecriture directe de la réponse

Une BlockAction peut directement écrire la réponse sans passer par une vue en utilisant la méthode write() de l'objet response. Cela pourra notamment être utilisé lorsque le flux XHTML est fourni par un service externe. Dans ce cas, execute() ne doit rien retourner (null).

class mymodule_MyBlockAction extends website_BlockAction
{
  /**
   * @see f_mvc_Action::execute()
   *
   * @param f_mvc_Request $request
   * @param f_mvc_Response $response
   * @return String
   */
  function execute($request, $response)
  {
    $html = ...;
    $response->write($html);
    return null;
  }
}

Rediriger vers un autre bloc

A la fin de son execution, un bloc peut rediriger vers un autre bloc. Typiquement, l'action prenant en compte l'insertion d'un nouveau document (ou plus généralement recevant un POST), aura intérêt à rediriger vers l'action affichant le résultat plutôt que de se contenter d'afficher la vue “résultat”. Ainsi, on évitera le symptôme “rejouer l'action” lorsque l'utilisateur rafraîchira sa page de résultat.

La redirection repose sur le fait que la page contenant le bloc visé soit taguée avec le tag contextuel du bloc (contextual_website_website_<NOM_MODULE>_<NOM_BLOC>). Si aucune page n'est trouvée, redirect() est équivalente à forward()

La méthode website_BlockAction::redirect() prend 4 paramètres dont deux optionnels :

  1. Le nom du module du bloc
  2. Le nom du bloc
  3. Les éventuels paramètres relatifs au module à transmettre
  4. Les éventuels paramètres “absolus” (sans mention au module) à transmettre
/**
 * @see f_mvc_Action::redirect()
 *
 * @param String $moduleName
 * @param String $actionName
 * @param Array<String, String> $moduleParams
 * @param Array<String, String> $absParams
 */
public final function redirect($moduleName, $actionName, $moduleParams = null, $absParams = null);

Ainsi, pour rediriger vers le bloc Mydocument du module “mymodule” (autrement dit la classe mymodule_BlockMydocumentAction), un bloc pourra en fin de sa méthode execute() procéder comme suit :

$this->redirect("mymodule", "mydocument", array("documentId" => 123), array("otherParam" => "toto"));

Si URL_PAGE.html est l'URL de la page tagguée contextual_website_website_mymodule_mydocument dans le site courant, l'URL vers laquelle le client sera redirigé sera : URL_PAGE.html?mymoduleParam[“documentId”]=123&otherParam=toto

Rediriger vers une méthode execute d'un bloc

Le paramètre $actionName de redirect() permet de spécifier la méthode execute du bloc à exécuter.

Ainsi la ligne suivante redirigera vers la page taguée contextual_website_website_rbs_componenttest et la méthode executeRun() du bloc rbs_BlockComponenttestAction sera exécutée :

$this->redirect("rbs", "componenttest.run", array("componenttestId" => 123));

Rediriger vers une ancre donnée de la page

Le paramètre $actionName de redirect() permet de spécifier une ancre :

$this->redirect("rbs", "componenttest.run#feature456", array("componenttestId" => 123));

L'URL générée sera complétée de #feature456, permettant ainsi au navigateur client de positionner le viewport à l'ancre considérée.

N.B.: l'ancre doit toujours être le dernier élément du paramètre $actionName.

Proposer des métadonnées au rédacteur de page

Un bloc peut mettre à disposition des métadonnées que le rédacteur utilise pour renseigner les propriétés “titre de page”, “description” et “mots-clefs” de la page le contenant.

Ainsi le bloc “post” (ou “Détail d'un billet”) propose des métadonnées comme “titre du billet”, “description du billet” ou “mots-clefs du billet” :

Deux étapes sont nécessaires pour qu'un bloc propose des métadonnées :

  1. Déclarer les métadonnées proposées par le bloc, dans la configuration
  2. Implémenter la méthode getMetas() dans l'action, qui doit retourner un tableau des valeurs des métadonnées,

Exemple : déclaration des métadonnées proposées par le bloc “billet” du module “blog” (dans config/blocks.xml) :

<block type="modules_blog_post" ...>
  <parameters>
    ...
  </parameters>
  <metas>
    <meta name="postLabel" allow="title,description" />
    <meta name="postDate" allow="title,description" />
    <meta name="postSummary" allow="description" />
    <meta name="blogLabel" allow="title,description" />
    <meta name="blogDescription" allow="description" />
    <meta name="postKeywords" allow="keywords" />
    <meta name="postCategories" allow="keywords" />
  </metas>
</block>

Le tableau suivant présente les attributs de l'élément <meta/>, tous obligatoires :

Nom Valeurs possibles Description
name chaîne de caractères libre identifiant de la métadonnée, unique au sein du bloc
allow combinaisons de “title”, “description” et “keywords”, séparés par des virgules liste des propriétés de la page où la métadonnée est utilisable

A chaque métadonnée de nom “name” correspond une clef de localisation “modules.<moduleName>.bo.blocks.<blockName>.metas.<name>”.

Et du côté de l'action :

class blog_BlockPostAction extends website_BlockAction
{
  function getMetas()
  {
    ...
    return array("postLabel" => $postLabel, "blogLabel" => $blogLabel, ...);
  }
}

Déléguer à un autre bloc

Un bloc peut déléguer l'affichage à un autre bloc en utilisant la méthode forward(). Le “sous-bloc” est alors exécuté avec une requête qui partage l'ensemble des paramètres et attributs de la requête courante.

La méthode forward() prend deux arguments :

  1. le module du bloc destination
  2. le nom du bloc destination
/**
 * @see website_BlockAction::execute()
 *
 * @param f_mvc_Request $request
 * @param f_mvc_Response $response
 * @return String
 */
function execute($request, $response)
{
  // ... some code
  $this->forward("mymodule", "mydocument");
}

N.B. : forward() ne retourne rien : le résultat de l'exécution du bloc (affichage de la vue compris !) est directement affiché comme si le bloc “hôte” avait écrit le résultat de l'exécution sur la réponse.

La déléguation à un autre bloc peut se faire depuis un gabarit en utilisant l'extension PHPTal change:block

Valider les données de la requête

Avant d'exécuter executeXXX(), le controlleur (website_BlockController) interroge (si elle existe) la méthode validateXXXInput() du bloc :

  • si celle-ci renvoie true, alors seulement executeXXX() sera exécutée
  • sinon, la méthode getXXXInputViewName() est appelée pour déterminer le nom de la vue à afficher et celle-ci est rendue.

Optionnellement, le bloc peut implémenter validateInput() pour conditionner l'appel à execute() : si validateInput() retourne false, execute() n'est pas exécutée et la méthode getInputViewName() détermine la vue à afficher. L'implémentation par défaut de validateInput() retourne true.

Validation déclarative - implémentation par défaut de validateXXXInput

La validation des données peut souvent se régler en utilisant des validateurs du framework.

L'implémentation par défaut de validateXXXInput() utilise la méthode getXXXInputValidationRules() dont le rôle est de retourner des règles de validation, du type “<parameterName>{<validatorName>:<validatorParam>}”, comme dans la définition XML d'un document.

Voici un exemple qui valide les données d'entrée de la méthode executeSave() en forçant :

  • “label” a être fourni et d'une taille inférieure ou égale à 255 caractères
  • “status” a être d'une taille inférieure ou égale à 10 caractères
  • “email” a être une adresse e-mail syntaxiquement valide
/**
 * @param f_mvc_Request $request
 * @return String[]
 */
function getSaveInputValidationRules($request)
{
  return array("label{blank:false;maxSize:255}", "status{maxSize:10}", "email{email:true});
}

Ré-utiliser les contraintes déclarées sur un document

Il est possible de récupérer tout ou partie des contraintes déclarées sur un document donné en utilisant BeanUtils::getBeanValidationRules(). Le tableau retourné est directement utilisable comme retour de getXXXInputValidationRules()

La méthode BeanUtils::getBeanValidationRules() prend trois arguments dont deux optionnels :

  1. $beanClassName : le nom de la classe du document
  2. $include : le tableau optionnel des noms de propriété desquelles les contraintes doivent être importées
  3. $exclude : le tableau optionnel des noms de propriété desquelles les contraintes ne doivent pas être importées
/**
 * @param String $beanClassName
 * @param String[] $include
 * @param String[] $exclude
 * @return String[]
 */
static function getBeanValidationRules($beanClassName, $include = null, $exclude = null)

Ainsi, pour récupérer les contraintes déclarées et du document mymodule_persistentdocument_mydoc, sauf pour la propriété project, on procédera comme suit :

function getSaveInputValidationRules($request)
{
  return BeanUtils::getBeanValidationRules("mymodule_persistentdocument_mydoc", null, array("project"));
}

Les méthode getXXXInputValidationRules() reçoivent en argument la requête, ce qui leur permet selon la valeur de certains attributs de valider ou non telle ou telle partie des données.

function getSaveInputValidationRules($request, rbs_persistentdocument_component $component)
{
  if ($component->isNew())
  {
    // project property will be initialized later.
    return BeanUtils::getBeanValidationRules("rbs_persistentdocument_component", null, array("project"));
  }
  return BeanUtils::getBeanValidationRules("rbs_persistentdocument_component");
}

Valider des paramètres "à la main"

Lorsque la validation déclarative ne suffit pas pour exprimer l'ensemble des contraintes, il faut ré-implémenter la méthode validateXXXInput() et retourner true ou false selon les cas.

Si certaines validation peuvent s'exprimer déclarativement, on peut alors utiliser la méthode website_BlockAction::processValidationRules() :

function validateSaveInput($request, mymodule_mybean $component)
{
   // declarative validation
   $rules = array("label{blank:false;maxSize:255}", "status{maxSize:10}");
   $isOk = $this->processValidationRules($rules, $request, $component);
   // non-declarative validation
   $myCustomOk = $component->isSomethingMethod($request);
   return $isOk && $myCustomOk;
}

N.B. : on peut aussi, si cela a du sens, déclarer son propre validateur et l'utiliser dans une règle.

Transmettre des messages

Transmettre des messages utilisateurs comme de simples messages de confirmation ou des messages d'erreur se fait avec les méthodes suivantes :

  • website_BlockAction::addMessage($msg),
  • website_BlockAction::addError($msg).

Ces méthodes définissent des attributs de l'objet Page (website_BlockAction::getContext() : website_Page) qui sont notamment utilisés par les extensions change:messages et change:errors pour la restitution des messages dans la vue.

Les messages transmis ne sont pas traduits : si vos messages sont internationalisés, il faudra utiliser f_Locale::translate() en amont de l'appel à addMessage() ou addError().

$this->addMessage(f_Locale::translate("&mymodule.bo.someMessage;"));
$this->addMessage(f_Locale::translate("&mymodule.bo.someOtherMessage;"));

N.B.: Lors de la validation déclarative des paramètres d'entrée de la méthode execute(), les éventuels messages d'erreurs sont automatiquement ajoutés en suivant le même mécanisme.

   ...
   <ul change:messages="" />
   ...

va générer

   ...
   <ul class="messages">
      <li>un certain message</li>
      <li>un certain autre message</li>
   </ul>
   ...

Cas des formulaires multiples

Dans le cas où votre bloc affiche plusieurs formulaires et que vous souhaitez segmenter les messages utilisateurs, il faudra utiliser le second argument optionnel $relKey :

Côté bloc :

$this->addMessage(f_Locale::translate("&mymodule.bo.someMessage;"), "form1");
$this->addMessage(f_Locale::translate("&mymodule.bo.someOtherMessage;"), "form2");

Côté gabarit :

  <form change:form id="form1">
     <!-- affiche someMessage traduit -->
     <ul change:messages="relKey form1" />
  </form>
 
  <form change:form id="form2">
     <!-- affiche someOtherMessage traduit -->
     <ul change:messages="relKey form2" />
  </form>

Regrouper le fonctionnel commun en un bloc

Un bloc peut éventuellement être responsable de plusieurs traitement distincts comme l'affichage du détail, l'ajout, l'édition, la suppression ou encore le listing de documents d'un modèle donné.

Pour simplifier le code, éviter la multiplication des classes et permettre plus facilement la mutualisation de portions de code, un bloc peut déclarer plusieurs méthodes de type “execute”. Il pourra ainsi implémenter les méthodes execute(), executeList(), executeSave(), executeDelete(), ou tout autre méthode nommée execute<Action>().

Le contrôleur de blocs prendra la main pour décider quelle méthode devra être appelée (économisant ainsi une série de if ou de switch dans la méthode execute du bloc).

A chaque méthode executeXXX() sont associées les méthodes :

  • validateXXXInput(),
  • getXXXInputViewName(),
  • getXXXBeanInfo().

Chaque “sous action” a donc son cycle d'exécution séparé des autres.

Processus de sélection d'une sous-action

La sélection de la méthode du bloc à exécuter se fait en fonction du paramètre de requête website_BlockAction::SUBMIT_PARAMETER_NAME : si ce paramètre est présent et défini pour un bloc une valeur alors cette valeur donne le nom de la méthode à exécuter.

En général, le développeur ne renseigne pas directement ce paramètre : les extensions PHPTal change:submit et change:actionlink le définissent correctement en fonction de leurs paramètres :

  • pour change:submit, le paramètre name permet de spécifier la “sous-action” cible,
  • pour change:actionlink, le paramètre action permet spécifier la “sous-action” cible.

Exemple

Le bloc rbs_BlockProjectAction est responsable de :

  • l'affichage du détail d'un projet (méthode execute, définie par generic_DetailBlockAction)
  • l'insertion et la mise à jour d'un projet
    • Affichage du formulaire d'insertion (méthode executeAddForm)
    • Affichage du formulaire d'édition (méthode executeEditForm)
  • l'effacement d'un projet (méthode executeDelete)

Ci-dessous la classe du bloc :

class rbs_BlockProjectAction extends generic_DetailBlockAction
{
        /**
         * @see website_BlockAction::execute()
         *
         * @param f_mvc_Request $request
         * @param f_mvc_Response $response
         * @return String
         */
        function executeAddForm($request, $response, rbs_persistentdocument_project $project)
        {
                return $this->getSaveInputViewName();
        }
 
        /**
         * @see website_BlockAction::execute()
         *
         * @param f_mvc_Request $request
         * @param f_mvc_Response $response
         * @return String
         */
        function executeEditForm($request, $response, rbs_persistentdocument_project $project)
        {
                return $this->getSaveInputViewName();
        }
 
        function getSaveInputViewName()
        {
                return "Form";
        }
 
        function getSaveInputValidationRules($request)
        {
                return BeanUtils::getBeanValidationRules("rbs_persistentdocument_project");
        }
 
        /**
         * @see website_BlockAction::execute()
         *
         * @param f_mvc_Request $request
         * @param f_mvc_Response $response
         * @return String
         */
        function executeSave($request, $response, rbs_persistentdocument_project $project)
        {
                $isNew = $project->isNew();
                $project->save();
                if ($isNew)
                {
                        return $this->redirect("rbs", "projectlist");
                }
                return $this->redirect("rbs", "project", array("projectId" => $project->getId()));
        }
 
        /**
         * @see website_BlockAction::execute()
         *
         * @param f_mvc_Request $request
         * @param f_mvc_Response $response
         * @return String
         */
        function executeDelete($request, $response)
        {
                $project = $this->getDocumentParameter("projectId");
                if ($project !== null)
                {
                        $project->delete();
                }
                return $this->redirect("rbs", "projectlist");
        }
}

Le gabarit utilisé pour le détail d'un projet (Rbs-Block-Project-Success.all.all.html). Celui-ci affiche les propriétés du projet et présente des liens pour :

  • Lister les projets (fonctionnalité prise en charge par un autre bloc : rbs_BlockProjectlistAction),
  • Editer le projet,
  • Effacer le projet.
<tal:block change:loadhandler="generic_DocumentLoadHandler" args="project, projectId" />
 
<h2 change:h="">${project/getLabel}</h2>
 
<p>
        <a change:actionlink="" block="rbs_projectlist" class="link">Liste des projets</a>
</p>
 
<dl>
        <tal:block tal:condition="project/getLabel">
                <dt><strong change:translate="&amp;modules.rbs.document.project.Label;" /></dt>
                <dd>${project/getLabel}</dd>
        </tal:block>
 
        ...
 
        <tal:block tal:condition="project/getAttachmentsArray">
                <dt><strong>Pièces jointes</strong></dt>
                <dd>
                        <ul class="normal">
                                <li tal:repeat="attachement project/getAttachmentsArray">
                                        <a change:link="document attachement">${attachement/getLabel}</a>
                                </li>
                        </ul>
                </dd>
        </tal:block>
</dl>
 
<ul class="actionmenu">
        <li>
                <a change:actionlink="" action="editForm" projectId="${project/getId}">Editer <em>${project/getLabel}</em></a>
        </li>
        <li>
                <a change:actionlink="" action="delete" projectId="${project/getId}"
                 change:i18attr="onclick &modules.rbs.document.project.confirm-delete-js;">Effacer <em>${project/getLabel}</em></a>
        </li>
</ul>
 
...

Le gabarit utilisé pour le formulaire de création/édition (Rbs-Block-Project-Form.all.all.html). Du fait du nom de l'élément change:submit (“save”), l'action du formulaire est la méthode executeSave du bloc :

<tal:block change:loadhandler="generic_DocumentLoadHandler" args="project, projectId" />
<tal:block tal:define="global isNew php:!project or project.isNew()" />
 
<h2 change:h="" tal:condition="isNew">Créer un projet</h2>
<h2 change:h="" tal:condition="not: isNew"><a change:link="document project">${project/getLabel}</a> : édition</h2>
 
<p>
        <a change:actionlink="" block="rbs_projectlist" class="link">Liste des projets</a>
</p>
 
<form change:form="beanClass rbs_persistentdocument_project; beanName project">
        <div change:errors=""></div>
        <tal change:field="name beanId" hidden="true" />
        <ol>
                <li><input change:field="name label" /></li>
                <li><input change:field="name name" /></li>
                <li><input change:field="name description" /></li>
                <li><input change:field="name version" /></li>
                <li class="last"><input change:uploadfield="name attachments" label="Pièce(s) jointe(s) : " 
                 class="button" action="${php: isNew ? 'addForm' : 'editForm'}" /></li>
        </ol>
        <p>
                <input change:submit="" name="save" value="Enregistrer" class="button" />
        </p>
</form>

Créer un lien vers un bloc

L'extension PHPTal change:actionlink permet la création de liens vers une page contenant un bloc donné, en lui transmettant des paramètres. La page contenant le bloc est supposée être taguée avec le tag associé au bloc (contextual_website_website_modules_<NOM_BLOC>).

Exemple : lien vers le bloc rbs_BlockProjectAction, méthode executeEditForm. Un paramètre supplémentaire, projectId, est transmis avec.

<a change:actionlink="" block="rbs_project" action="editForm"
   projectId="${project/getId}">Editer <em>${project/getLabel}</em></a>

Le paramètre block

  • Tag associé à un bloc. Par défaut contextuel (contextual_website_website_modules_<NOM_BLOC>),
  • Si vide, vers le bloc courant.

Le paramètre action :

  • Optionnel. Si non fourni, méthode execute appelée.

Paramètres supplémentaires

  • en valeur de l'attribut actionlink,
  • ou en attribut de l'élément XML,
  • complété automatiquement avec le module du bloc cible (<nomModule>Param[<param>]) pour ne transmettre qu'au module.

Réagir à l'insertion dans le contenu d'une page

interface website_PageBlock

/**
 * Called when the block is inserted into a page content
 * @param website_persistentdocument_Page $page
 */
function onPageInsertion($page);

Réagir au retrait du contenu d'une page

interface website_PageBlock

/**
 * Called when the block is removed from a page content
 * @param website_persistentdocument_Page $page
 */
function onPageRemoval($page);
ref/blocs/cas_d_utilisation.txt · Dernière modification: 2017/01/19 14:54 (modification externe)