La structure de base de données

Nous aurons besoin de deux tables pour stocker nos articles :

  • une avec l'information de base des article (auteur, titre, résumé, date de publication)
  • et une contenant les paragraphes (un par enregistrement, avec le titre et le contenu).

Techniquement, nous pourrions employer juste une table, utilisant quelques délimiteurs spéciaux incorporés dans le texte (tel que le « ====FIN DE PAGE====") pour découper les paragraphes, mais croyez moi qu'à la longue cette une solution bien meilleure

[SQL]
CREATE TABLE articles (
  id INTEGER NOT NULL,
  title VARCHAR(250) NOT NULL,
  abstract TEXT,
  submission_date TIMESTAMP NOT NULL,
  author_id INTEGER NOT NULL,
  CONSTRAINT articles_pkey PRIMARY KEY(id)
);

CREATE TABLE paragraphs (
  article_id INTEGER NOT NULL,
  paragraph_id INTEGER NOT NULL,
  title VARCHAR(250),
  content TEXT,
  CONSTRAINT paragraphs_pkey PRIMARY KEY(article_id, paragraph_id),
  CONSTRAINT paragraphs_fk FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE
);

Données d'exemple

Voici quelques données d'exemple si vous voulez tester tout en lisant ce tuto :

[SQL]
-- Premier Article
INSERT INTO articles (id, title, abstract, submission_date, author_id) VALUES (
  1,
  'How to navigate through the paragraphs of an article with Pager',
  'You\'ve probably seen a lot of websites featuring long, detailed articles, which are split into paragraphs, each of them in a separate page. Users often prefer reading short chunks of text instead of scrolling a very long page (unless they want to print the page, then the opposite applies). In this tutorial, we\'re going to see how we can build an article pagination system, with a little help from PEAR::Pager.',
  CURRENT_TIMESTAMP,
  1
);
INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 1, 'The database structure', 'First paragraph here');
INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 2, 'Sample data', 'Second paragraph here');
INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 3, 'Showtime', 'Third paragraph here');
INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 4, 'Alternate navigation', 'Fourth paragraph here');
INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 5, 'Article summary', 'Fifth paragraph here');
INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 6, 'Printer friendly version', 'Sixth paragraph here');

-- Second Article
INSERT INTO articles (id, title, abstract, submission_date, author_id) VALUES (
  2,
  'PEAR::Pager tutorials',
  'New series of tutorials about PEAR::Pager',
  CURRENT_TIMESTAMP,
  1
);
INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (2, 1, 'Articles index', '1. How to efficiently paginate database results.  2. Create pretty links with Pager and mod_rewrite.  3. Navigation with Pager and AJAX (or simple Javascript).  4. Article pagination.');

Showtime!

Maintenant que nous sommes ok avec la structure de base de données, nous pouvons afficher les articles sur notre site, un paragraphe par la page.

Pour cette tâche, nous allons employer PEAR::MDB2 DBAL et le bien pratique Pager_Wrapper que nous avons déjà vu dans un tuto précédent :

[php]
<?php
//Copier le fichier Pager_Wrapper file là où il peut-être inclu
require_once 'Pager_Wrapper.php';
require_once 'MDB2.php';

$article_id = 1; //if you fetch this parameter via GET/POST, remember to validate it!

//on passe le code de connexion DB 
//on suppose qu'on a une connexion valide dans  $db

$pager_options = array(
    'mode'       => 'Sliding',
    'perPage'    => 1, //On ne veut qu'un paragraphe par page
    'delta'      => 3,
);

$query = 'SELECT articles.title AS article_title,
                 articles.submission_date,
                 articles.abstract,
                 paragraphs.title AS paragraph_title,
                 paragraphs.content
            FROM paragraphs
       LEFT JOIN articles ON articles.id = paragraphs.article_id
           WHERE articles.id = '. (int)$article_id .'
        ORDER BY paragraphs.paragraph_id;

$paged_data = Pager_Wrapper_MDB2($db, $query, $pager_options);

//Afficher les résultats
echo '<h1>'.$paged_data['data'][0]['article_title'].'</h1>';
echo '<p><i>'.$paged_data['data'][0]['submission_date'].'</i></p>';
if ($paged_data['page_numbers']['current'] == 1) {
    // afficher aussi le résumé.
    echo '<p>'.$paged_data['data'][0]['abstract'].'</p>';
}
echo '<h2>'.$paged_data['data'][0]['paragraph_title'].'</h2>';
echo '<p>'.$paged_data['data'][0]['content'].'</p>';

//afficher les liens
echo $paged_data['links'];
?>

Puisque nous avons demandé au paginateur de couper les articles (les paragraphes, dans notre cas) en groupes de "un", il renverra seulement un paragraphe, et nous pouvons naviguer vers les autres paragraphes avec les liens générés par paginateur.

Voici le rendu du script :

Pager with links example


Autre navigation

Si vous n'aimez pas des liens normaux pour la navigation, et préférez un menu de <select> à la place, vous devez ajouter un appel à la la fonction getPageSelectBox () dans Pager_Wrapper_MDB2, avant de renvoyer le tableau de données paginée, puisqu'elle n'est pas incluse dans la version par défaut :

[php]
function Pager_Wrapper_MDB2(&$db, $query, $pager_options = array(), $disabled = false, $fetchMode = MDB2_FETCHMODE_ASSOC)
{
  // Pager_Wrapper_MDB2() body omitted. Add the following lines before the return call:

  // ===== Début du  CODE ajouté  ======
  $selectbox_options = array(
      'optionText' => 'page %d',
      'autoSubmit' => true,
  );
  $page['select_menu'] = $pager->getPageSelectBox($selectbox_options);
  // ===== Fin du  CODE ajouté =====

  return $page;
}

Maintenant on affiche le menu de navigation :

[php]
<?php
// [snip: même code que dans le paragraphe précédent]

//Affichage du menu
echo 'Choisissez une page: '. $paged_data['select_menu'];
?>

Et voilà ce que ca devrait donner: Pager avec exemple de menu select


Sommaire de l'article

Parfois, vous pouvez vouloir montrer un sommaire de l'article, avec une liste des titres de paragraphe, chacun indiquant le paragraphe complet.

Aucun problème, vous pouvez faire cela.

[php]
<?php
$query = 'SELECT title
            FROM paragraphs
           WHERE article_id = '. (int)$article_id
     .' ORDER BY paragraph_id';
$paragraph_titles = $db->queryCol($query);
//on oublide le code de vérification

echo '<h2>Sommaire</h2>';
echo '<ul>';
foreach ($paragraph_titles as $k => $title) {
    if ($k == ($paged_data['page_numbers']['current'] -1)) {
        // Page courrante, ne pas afficher le lien
        echo '<li>' . $title . '</li>';
    } else {
        echo '<li><a href="article.php?id='. (int)$article_id .'&pageID='. ($k+1) .'">'. $title . '</a></li>';
    }
}
echo '</ul>';
?>

Le résultat est un résumé des titres de paragraphe, avec leurs liens Exemple de sommaire


Version imprimable

Si vous voulez proposer une version pour l'impression avec l'article complet, vous pouvez chercher tous les paragraphes et simplement les afficher l'un après l'autre :

[php]
<?php
$query = 'SELECT title,
                 submission_date,
                 abstract
            FROM articles
           WHERE id = '. (int)$article_id;
$article = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);

$query = 'SELECT title,
                 content
            FROM paragraphs
           WHERE article_id = '. (int)$article_id
     .' ORDER BY paragraph_id';
$paragraphs = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);

//Afficher les données principales de l'article:
echo '<h1>'.$article['title'].'</h1>';
echo '<p><i>'.$article['submission_date'].'</i></p>';
echo '<p>'.$article['abstract'].'</p>';


// Afficher les paragraphes de l'article:
foreach ($paragraphs as $paragraph) {
    echo '<h2>'.$paragraph['title'].'</h2>';
    echo '<p>'.$paragraph['content'].'</p>';
}

?>

J'espère que ce tuto était utile (la traduc aussi :).

Si vous avez besoin de quelques clarifications, ou avez quelques suggestions , envoyez-nous un mail :

  • Lorenzo de préférence mais en anglais.
  • Moi de préférence mais en anglais.