Nette framework : utilisation

Ici, nous voyons les interactions entre une présentation et ses vues, comment fonctionnent les formulaires, ainsi que l'ajout et l'édition de données dans la base de données.

Avant de commencer, admettons que vous ayez connecté votre base de données (cf. article précédent) en prenant soin de privilégier InnoDB comme moteur de stockage, et que vous ayez créé deux tables : posts et comments.

Presenter / Présentation

app/presenters/PostPresenter.php


namespace App\Presenters;

use Nette,
    Nette\Application\UI\Form;

class PostPresenter extends BasePresenter
{
/** @var Nette\Database\Context */
private $database;

public function __construct(Nette\Database\Context $database)
{
    // connexion à la base de données
    $this->database = $database;
}

public function renderDefault() ➊
// génère une vue default.latte
{
   // récupère les données et
   // les envoie dans la vue sous la variable $posts
   $this->template->posts = $this->database->table('posts')
      ->order('created_at DESC')
      ->limit(5);
}

public function renderShow($postId) ➋
// génère une vue show.latte
{
   // récupération du $postId,
   // s'il n'existe pas, afficher une erreur,
   // sinon envoyer le résultat dans la vue
   $post = $this->database->table('posts')->get($postId);
   if (!$post) {
       $this->error('Post not found');
   }

   $this->template->post = $post;
   $this->template->comments = $post->related('comments')
       ->order('created_at');
}

protected function createComponentCommentForm() ➌
// sera récupéré dans la vue sous "CommentForm"
{
    // création d'un formulaire
    $form = new Form;

    $form->addText('name', 'Your name:') // ('name', 'label')
        ->setRequired() // required
        ->setAttribute('class', 'toto')
        ->setAttribute('placeholder', 'Please type your name')
        ->setAttribute('onchange', 'mafonction()');

    $form->addText('email', 'Email:')
        ->setType('email');

    $sex = array(
        'm' => 'male',
        'f' => 'female',
    );
    $form->addRadioList('gender', 'Gender:', $sex);

    $form->addCheckbox('Human', 'Are you a human ?');

    $countries = array(
        'Europe' => array(
            'CZ' => 'Czech republic',
            'SK' => 'Slovakia',
            'GB' => 'United Kingdom',
        ),
        'CA' => 'Canada',
        'US' => 'USA',
        '?'  => 'other',
    );
    $form->addSelect('country', 'Country:', $countries)
        ->setPrompt('Pick a country');

    $form->addTextArea('content', 'Comment:')
       ->setDisabled(); // disabled

    $form->addUpload('thumbnail', 'Thumbnail:')
        ->addRule(Form::IMAGE, 'Thumbnail must be JPEG, PNG or GIF');

    $form->addSubmit('send', 'Publish comment');

    $form->onSuccess[] = array($this, 'commentFormSucceeded'); ➍

    return $form;
}

➍ public function commentFormSucceeded($form, $values) {
$postId = $this->getParameter('postId');

$this->database->table('comments')->insert(array(
     'post_id' => $postId,
     'name' => $values->name,
     'email' => $values->email,
     'content' => $values->content,
));

$this->flashMessage('Thank you for your comment', 'success'); ➎
$this->redirect('this');
// alternative :
// $this->redirect('show', $post->id);
}

public function actionEdit($postId) ➒
// actionEdit et non renderEdit :
// il ne s'agit pas que d'un travail de rendu dans ce cas-ci.
{
    $post = $this->database->table('posts')->get($postId);
    if (!$post) {
        $this->error('Post not found');
    }
    $this['postForm']->setDefaults($post->toArray()); ➏
}

protected function createComponentPostForm() ➏
{
$form = new Form;
$form->addText('title', 'Title:')
     ->setRequired();
$form->addTextArea('content', 'Content:')
     ->setRequired();

$form->addSubmit('send', 'Save and publish');

$form->onSuccess[] = array($this, 'postFormSucceeded'); ➐

return $form;
}

➐ public function postFormSucceeded($form, $values) {
$postId = $this->getParameter('postId');

if ($postId) {
   $post = $this->database->table('posts')->get($postId);
   $post->update($values);
} else {
   $post = $this->database->table('posts')->insert($values);
}

$this->flashMessage('Post was published', 'success'); ➎
$this->redirect('show', $post->id);
}

}

View / Vue

app/templates/Post/default.latte


{block content} ➑
    <h1 n:block="title">Blog</h1>

    {foreach $posts as $post}
    <div class="post">
        <div class="date">{$post->created_at|date:'F j, Y'}</div>

        <h2>{$post->title}</h2>

        <div>{$post->content}</div>
    </div>
    {/foreach}

    <a n:href="Post:create">Write new post</a>

{/block}

➋ app/templates/Post/show.latte


{block content} ➑
<p><a n:href="Homepage:default">← back to posts list</a></p>
<div class="date">{$post->created_at|date:'F j, Y'}</div>
<h1 n:block="title">{$post->title}</h1>
<div class="post">{$post->content}</div>
➒ <a n:href="edit $post->id">Edit this post</a>

<h2>Post new comment</h2>
➌ {control commentForm}

<h2>Comments</h2>
<div class="comments">
    {foreach $comments as $comment}
        <p><b><a href="mailto:{$comment->email}"
         n:tag-if="$comment->email">
         {$comment->name}</a></b> said:</p>
        <div>{$comment->content}</div>
    {/foreach}
</div>

app/templates/Post/edit.latte


{block content} ➑
<h1>Edit post</h1>

➏ {control postForm}

app/templates/@layout.latte (gabarit inclusif)


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>{ifset title}{include title|striptags} | {/ifset}Nette Web</title>
    <!-- embarque/agrège le title spécifié dans la vue -->

    <link rel="shortcut icon" href="{$basePath}/favicon.ico">
</head>

<body>
     ➎ <div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
            {$flash->message}
        </div>
    <!-- affiche les messages d erreur liés
    à la complétion du formulaire (champs requis et autres règles)-->

     ➑ {include content}
        <!-- embarque/agrège le contenu des vues : {block content} -->

    {block scripts}
    <script src="//nette.github.io/resources/js/netteForms.min.js"></script>
    {/block}
</body>
</html>

Formulaires : validation et récupération des données


$form->addPassword('password', 'Password:')
    // if password is not longer than 5 characters ...
    ->addCondition(Form::MAX_LENGTH, 5)
        // ... then it must contain a number
        ->addRule(Form::PATTERN, 'Must contain number', '.*[0-9].*')
        ->addRule(Form::RANGE, 'You must be older 18 years and be under 120.', array(18, 120));

// récupération des valeurs après soumission :
$values = $form->getHttpData($form::DATA_TEXT, 'sel[]');
$values = $form->getHttpData($form::DATA_TEXT | $form::DATA_KEYS, 'sel[]');

Parmi les règles et conditions, voici la liste des fonctions possibles ((Liste non exhaustive.)) :

  • Form::MIN_LENGTH : minimum de caractères ou de fichiers accepté
  • Form::MAX_LENGTH : maximum de caractères ou de fichiers accepté
  • Form::LENGTH : nombre strict de caractères ou de fichiers accepté
  • Form::EMAIL : impose un format email
  • Form::URL : impose un format url
  • Form::INTEGER : impose un nombre entier
  • Form::FLOAT : impose un nombre décimal
  • Form::RANGE : impose une fourchette entre X et Y
  • Form::PATTERN : expression régulière
  • Form::BLANK : le champ doit rester vide
  • Form::MAX_FILE_SIZE : impose le poids d'un fichier
  • Form::MIME_TYPE : vérifie si le type MIME est valide
  • Form::IMAGE : impose un fichier au format .gif, .png ou .jpg
  • Form::FILLED : vérifie si le champ est rempli
  • Form::EQUAL : vérifie que la valeur est égale à celle prescrite
  • Form::NOT_EQUAL : vérifie que la valeur est différente de celle prescrite
  • Form::IS_IN : vérifie si la valeur fait partie d'un tableau
  • Form::VALID : vérifie si le champ est acceptable pour la validation

Sources