CMS Made Simple

Cet article est assez long mais montre la facilité, la flexibilité et la rapidité avec laquelle vous pourrez développer votre projet avec CMS Made Simple (CMSMS).

CMS Made SimpleSuivant le conseil de Benoît Henry à propos des CMS, j’ai choisi CMSMS pour ma première expérience. J’avais jusque là développé mes propres CMS. Les développeurs Web considèrent que CMSMS est intuitif et felxible. De plus, vous trouverez des articles très bien commentés sur le Wiki officiel de CMSMS.

CMSMS est une solution PHP-MySql Open Source que j’utiliserais pour des sites web aux exigences conventionnelles, pour des organisations sans but lucratif, par exemple, puisqu’il nécessite un temps de développement court et une implémentation rapide. Vous pouvez le télécharger sur CMSMS.org.

Cet article a été réalisé sur base de CMSMS 1.6.6.

Le seul aspect négatif que je mentionnerais ici est qu’il faudra que le développeur Web mette son CMSMS et les modules, plugins,… à jour régulièrement pour profiter des corrections de bugs et de failles de sécurité.

J’ai configuré un CMSMS en quelques heures (le développement du layout avait été réalisé au préalable) avec les options suivantes :

  • url rewriting (urls dynamiques du type : http://www.mondomaine.be/notre_projet.htm)
  • des templates dynamiques avec Smarty (le moteur PHP de templates)
  • un moteur de recherche interne
  • une zone client privée (+ login avec captcha)
  • une page contact (avec PHP mail et le module CMSMailer)
  • des meta tags “description” and “keywords” générés automatiquement
  • des fils d’ariane (breadcrumb trails)

CMSMS fonctionne avec des modules que l’on installe pour ajouter des fonctionnalités telles que :

  • un générateur de formulaires
  • un formulaire avec champ captcha (vérifie que l’utilisateur est un être humain plutôt qu’un robot)
  • un calendrier

Vous trouverez tous les modules, plugins, templates disponibles sur CMSMS Forge.

Documentation

Pour ma première installation, j’ai choisi d’installer des données exemples, ce qui rend l’adaptation et la compréhension du fonctionnement de CMSMS plus aisées. Une fois installé, sélectionnez le Menu Disposition / Gabarits (templates) et éditez le gabarit pour comprendre comment le moteur de template (Smarty) fonctionne.

Les modules and Balises (tags) que vous trouverez dans le Menu Extensions sont fournis avec des fichiers d’aide qui détaillent l’implémentation de ces fonctionnalités.

Le wiki officiel de CMSMS offre des articles bien documentés sur tout ce qu’il faut savoir pour adapter CMSMS à vos besoins.

Caractéristiques de développement Web

Implémenter CMSMS nécessite le développement Web de templates et feuilles de style css, le paramétrage des formulaires, modules choisis,… et la définition des accès administrateur par la gestion des groupes, utilisateurs et permissions. CMSMS est flexible et vous pouvez créer de nouveaux groupes d’administrateurs et définir leurs permissions de sorte que votre client n’aie pas la possibilité de rendre lui-même son site inaccessible. Vous pouvez par exemple paramétrer les droits d’éditeurs de sorte qu’ils ne puissent que modifier le contenu des pages. Ils ne pourront ainsi pas modifier vos scripts et paramétrages.

Les templates Smarty de CMSMS

Une caractéristique majeure de CMSMS est l’emploi de templates pour générer les contenus des pages. Les templates sous CMSMS sont accessibles par le Menu Disposition > Gabarits. CMSMS utilie Smarty, un moteur de template qui implémente votre template de manière dynamique grâce au code suivant, avec les variables placées entre accolades :

{metadata}

Mettez cette ligne de code entre les balises <head> et </head> et cette variable affichera les méta-données globales définies dans le Menu Administration de site > Paramètres globaux > Méta-données globales. Vous faites ainsi apparaître les balises suivantes sur toutes vos pages dynamiquement :

<meta http-equiv=”Content-type” content=”text/html; charset=iso-8859-1″ />

<meta http-equiv=”Content-Language” content=”fr-be” />

<meta name=”robots” content=”index,follow” />

<base url=”http://127.0.0.1/ael/cmsms” />

<link rel=”shortcut icon” href=”./favicon.ico” />

La variable suivante placée dans le template entre les balises <head> et </head> est basée sur le plugin appelé “autometa” :

{autometa}

Le plugin autometa est téléchargeable à partir de CMSMS Forge : autometa. Téléchargez-le et placez le fichier .php dans le répertoire plugins de CMSMS. Une fois inséré dans la <head> de votre template, le plugin génèrera dynamiquement les balises meta description et meta keywords à partir du contenu de la page. Si vous voulez paramétrer vos meta tags page par page manuellement, plutôt que laisser autometa les générer lui-même, vous le pouvez également (cliquez sur l’onglet “Options” dans le contenu de la page en question et remplissez le champ “méta-données spécifiques pour cette page”). Tapez :

<meta name=”description” content=”description de la page” />

<meta name=”keywords” content=”mots-clefs,pour, cette, page” />

Si vous désirez insérer du javascript dans votre template, ajoutez le code entre les balises {literal} et {/literal} :

{literal}

<script type=”text/javascript” language=”javascript” src=”./js/empty_fields.js”></script>

{/literal}

Dans le Menu Disposition > Gabarits, vous pouvez aussi cliquer sur le bouton “CSS” pour attacher une feuille de style à votre template. Ajoutez ensuite :

{stylesheet}

à l’intérieur de la balise <head> de votre template pour insérer les feuilles de styles attachées à ce template.

Vous devrez au préalable créer les feuilles de style dans le menu DIsposition > Feuilles de style.

Si vous voulez ajouter des commentaires à votre template, utilisez les {* et *} :

{* voici comment ajouter un commentaire à votre template *}

CMSMS et url rewriting en un clin d’oeil

Si vous désirez que CMSMS exprime des urls plus élégantes du type : http://www.moncmsms.com/comment-parametrer-cmsms/templates.htm au lieu de http://www.moncmsms.com/index.php?page=comment-parametrer-cmsms, vous devez :

  • modifier quelques variables dans /config.php :

$config['url_rewriting'] = true;

$config['page_extension'] = ‘.htm’;

  • puis créez un fichier .htaccess dans le root de votre projet Web avec les lignes suivantes :
SetEnv MAGIC_QUOTES 0
SetEnv REGISTER_GLOBALS 0
SetEnv PHP_VER 5
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^moncmsms.be$ [NC]
RewriteRule ^(.*)$ http://www. moncmsms.be/$1 [QSA,L,R=301]
#Sub-dir e.g: /cmsms
RewriteBase /
# 301 Redirect all requests that don’t contain a dot or trailing slash to
# include a trailing slash
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_URI} !\.
RewriteRule ^(.*) %{REQUEST_URI}/ [R=301,L]
# Rewrites urls in the form of /parent/child/
# but only rewrites if the requested URL is not a file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ index.php?page=$1 [QSA]
Ajouter un fil d’ariane (breadcrumb trails) à votre template
Le fil d’ariane est considéré comme une balise par CMSMS. Vous pouvez faire afficher toutes les balises disponibles dans le Menu Extensions > Balises : cliquez sur la balise “breadcrumbs” pour voir comment elle fonctionne. Puis ajoutez simplement :

{breadcrumbs starttext=’Vous êtes ici’ root=’Home’ delimiter=’&raquo;’}
dans le body de votre template pour afficher le breadcrumbs trails dans lvos pages. Vous pouvez facilement l’adapter en changeant le contenu de ses attributs. Référez-vous à l’aide fournie par le Menu Extensions > Balises > Breadcrumbs.

Ajouter un moteur de recherche interne à votre template

 

Vous pouvez configurer le module de moteur de recherche interne dans Menu Extensions > Recherche et changer les paramètres de formulaire, le template de recherche et le template des résultats de la recherche. Puis ajoutez les lignes suivantes dans votre template de page :

<div id=”search”>
{search search_method=”post”}
</div>

Ajouter une structure de pages en accès privé à CMSMS

Pour ajouter une zone privée avec login et mot de passe à votre CMSMS, Vous devrez télécharger et installer 3 modules complémentaires :

  • FrontEndUsers (FEU) (vous autorisera à gérer les utilisateurs front-end, c’est-à-dire les internautes)
  • CustomContent (vous permet d’afficher différents contenus à vos différents utilisateurs front-end)
  • Captcha (à utiliser pout ajouter un test Captcha à votre formulaire de login)

Une fois décompressés, copiez les répertoires de ces modules dans le réperoire Modules de votre CMSMS. Puis installez ces modules par le Menu Extensions > Modules dans l’ordre cité ci-dessus (FrontEndUsers, puis CustomContent, et enfin Captcha).

FrontEndUsers (FEU)

Le Menu Utilisateurs/Groupes > Gestion des utilisateurs du site est maintenant disponible. Ouvrez-le et :

  • allez dans l’onglet “Propriétés de l’utilisateur” pour ajouter des propriétés au profil de l’utilisateur (une propriété appelée “nom” avec el type “texte” et une propriété appelée “email” avec le type “Adresse email”)
  • allez dans l’onglet “Groupes” pour créer un groupe appelé “client_users” et paramétrez vos propriétés (nom et email comme champs optionnels ou requis, et cochez “demandé lors de la récupération de l’identifiant” en ce qui concerne la propriété “email”)
  • allez dans l’onglet “Utilisateurs” et ajoutez un utilisateur pour ce groupe (n’oubliez pas de cocher le groupe du nouvel utilisateur). Puis vous devrez remplir les propriétés définies précédemment.

Créez ensuite la page Login. Allez dans le menu Contenu > Pages > Ajouter un nouveau contenu et créez la page login avec le contenu suivant :

{FrontEndUsers}

Puis associez cette nouvelle page avec votre template principal (par l’onglet “options”).

Votre CMSMS client affiche maintenant une page login avec formulaire d’identification et test Captcha.

Paramétrer les contenus de l’espace privé

Une fois que l’utilisateur est identifié, Le CMSMS client devrait afficher les entrées de menu supplémentaires : les pages de l’espace privé. Il est assez simple de le réaliser. Nous utiliserons le module CustomContent pour ce faire. Vous devrez créer un template privé à associer aux pages privées.

Vous pouvez copier le template principal de votre site en cliquant dur le bouton “copier”.

Au lieu de taper :

{content}

pour afficher le contenu de la page directement, tapez :

{if $ccuser->loggedin()}

{content}

{else}

Désolé, vous devez vous identifier d’abord

{/if}

Puis créez une page privée dans le Menu Contenu > Pages et associez-la avec le nouveau template privé dans l’onglet “Options”.

Votre contenu privé devrait être affiché correctement une fois identifié côté client.

Le gestionnaire de Menu

CMSMS contient un gestionnaire de menus pour éditer ou créer de nouveaux templates de menus.

Vérifiez d’abord quel menu est chargé dans votre template par défaut. Ouvrez le Menu Disposition / Gabarits et cherchez le contenu de la div id=”menus”. Imaginons que le template de menu soit cssmenu_ulshadow.tpl.

Ouvrez le Menu Disposition > Gestion de Menu. Votre cssmenu_ulshadow.tpl n’es pas éditable (mais en lecture seule). Importez ce menu dans votre base de données en cliquant sur le bouton “Importer”. Donnez un nom à ce nouveau template de menu (“test”, dans mon cas).

Editez le template de menu “test”. Vous y trouverez du code Smarty, le moteur de template. Recherchez-y la première boucle “Foreach” et ajoutez la ligne suivante juste après :

{$node->id}

Ensuite, vous devez changer le template de page par défaut pour qu’il prenne en charge votre menu “test”. Allez dans le Menu Disposition > Gabarits, et éditez le template par défaut (NClearBlue dans mon cas) et changez le contenu de la div menu en :

{*menu template=’cssmenu_ulshadow.tpl’*}

{menu template=”test1″}

J’ai simplement commenté le template de menu par défaut et ajouté mon nouveau template de menu.

Allez sur le site client, rafraîchissez la page et vous verrez apparaître l’ID numérique de la page privée (57 dans mon cas).

De retour au Menu Disposition > Gestion de Menu, éditez le menu “test”, et remplacez le {$node->id} par :

{if (($node->id == 57 and $ccuser->loggedin()) or $node->id <> 57)}

qui vérifiera si la page affichée est la zone privée. Si c’est le cas, l’utilisateur doit s’être préalablement identifié pour que le menu s’affiche.

La dernière chose à faire est f’ajouter :

{/if}

juste avant le {/foreach} en fin de fichier.

Webliographie :

Smarty.net

Smarty basics (tuto)

CMSMS Forge

CMSMS Wiki officiel

CMS Made Simple

This article is rather long but shows how easy, flexible and fast CMS Made Simple (CMSMS) can be.

CMS Made SimpleFollowing Benoît Henry‘s advice on CMS, I chose CMSMS for my first CMS experience. Before that, I had only developed my own CMS. Webdevelopers will consider it intuitve and flexible. You’ll find well-documented articles on CMSMS official Wiki.

CMSMS is a PHP-MySql Open Source solution I would implement for websites with straightforward requirements, for non-profit organization, for example, since it requires short development and quick implementation. You can download it at CMSMS.org.

This article is based on CMSMS v. 1.6.6.

The only negative aspect I would here mention is you’ll need to upgrade your CMSMS and modules, plugins,… implemented regularly for bug fixes and security fixes.

I managed to configure CMSMS for a website in a few hours (layout development had been prepared beforehand) with the following features :

  • url rewriting (dynamic urls of the type : http://www.mydomain.be/our_project.htm)
  • dynamic templates with Smarty (template engine for PHP)
  • internal search engine
  • private client area (+ login with captcha)
  • contact page (with PHP mail through CMSMailer)
  • meta tags description and keywords generated automatically
  • breadcrumb trails

CMSMS works with modules you install to add functionalities such as:

  • a form builder
  • a captcha form (test in form to check if user is a robot or a human being)
  • a calendar

You’ll find lots of modules, templates,… to be downloaded in CMSMS Forge.

Documentation

For my first install, I installed it with sample data which made adaptation much easier. Once installed, select Menu Layout / Templates and edit a template to check how the template engine (Smarty) works.

Modules and Tags (found in Menu Extensions) are provided with help files that detail the way you should implement functionalities.

CMSMS official Wiki offers well-documented articles on eveything you need to adapt CMSMS to your needs.

Webdevelopment features

Implementing CMSMS for a customer requires the webdeveloper to create the templates and css stylesheets, customize the website forms, thechosen modules,… and define administrators’ access through groups, users and permissions. CMSMS is flexible enough for you to create new groups of administrators and manage their rights so that they don’t set their website out of order. As an example, you can customize editors rights so that they can only modify page content and leave your scripting and parameters intact.

CMSMS Smarty templates

A key CMSMS feature is the use of templates to generate page content. Templates in CMSMS are accessible through menu Admin Menu layout > templates. CMSMS uses Smarty, a template engine that implements your template dynamically through the following kind of code, with the variable written inside curly brackets :

{metadata}

Put in between <head> and </head>, this variable prints global meta tags defined in menu Site Admin / Global Settings. That way, you can have all your pages display the following tags :

<meta http-equiv=”Content-type” content=”text/html; charset=iso-8859-1″ />

<meta http-equiv=”Content-Language” content=”fr-be” />

<meta name=”robots” content=”index,follow” />

<base url=”http://127.0.0.1/ael/cmsms” />

<link rel=”shortcut icon” href=”./favicon.ico” />

The next variable is based on the plugin called “autometa” :

{autometa}

This autometa plugin can be found in CMSMS Forge : autometa. Download it and place the php file in CMSMS plugins directory. Once inserted inside the <head> of your template, the plugin will generate meta description and meta keywords tags from the content of the page. If you want to customize your meta tags page per page rather than let autometa manage meta tags description and keywords (the latter being rather deprecated now), you can also do so (click the “Options” tab in the page content and fill in the “metadata for this page” field). Then type :

<meta name=”description” content=”description of the page content” />

<meta name=”keywords” content=”keywords, for, that, page” />

If you want to insert javascript in your template, add the code between {literal} and {/literal} tags :

{literal}

<script type=”text/javascript” language=”javascript” src=”./js/empty_fields.js”></script>

{/literal}

In the Admin menu Layout > Templates page, you may also click “CSS button” to attach a css stylesheet to the template. Then simply add :

{stylesheet}

in your template to implement the attached stylesheets in your <head>.

You’ll have to create your own stylesheets in Menu Layout > stylesheets beforehand.

If you want to add comment to your template, put it between {* any comment *}

{* this is how you add comments to your template *}

CMSMS and url rewriting in the twinkling of an eye

If you want CMSMS to render pretty urls of the following type : http://www.mycmsms.com/how-cmsms-works/content_types.htm instead of http://www.mycmsms.com/index.php?page=how-cmsms-works, you have to :

  • change some default variables in /config.php. Please set :

$config['url_rewriting'] = true;

$config['page_extension'] = ‘.htm’;

  • then create a .htaccess file at the root of your web project with the following lines :
SetEnv MAGIC_QUOTES 0
SetEnv REGISTER_GLOBALS 0
SetEnv PHP_VER 5
Options +FollowSymLinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^mycmsms.be$ [NC]
RewriteRule ^(.*)$ http://www. mycmsms.be/$1 [QSA,L,R=301]
#Sub-dir e.g: /cmsms
RewriteBase /
# 301 Redirect all requests that don’t contain a dot or trailing slash to
# include a trailing slash
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_URI} !\.
RewriteRule ^(.*) %{REQUEST_URI}/ [R=301,L]
# Rewrites urls in the form of /parent/child/
# but only rewrites if the requested URL is not a file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ index.php?page=$1 [QSA]
Add breadcrumb trails to your template
You can display all available tags in Menu Extensions / tags : a click on tag “breadcrumbs” will tell you what it’s worth. Simply add :

{breadcrumbs starttext=’You are here’ root=’Home’ delimiter=’&raquo;’}
to the body of your template to display breadcrumbs trails. You can easily adapt it by changing the content of its attributes. Just refer to the help provided in Menu Extensions / tags.

Add an internal search engine to your template

 

You can configure the internal search engine module in Menu Extensions / Search and change the form parameters, the search template and the result template. Then simply add the following lines in your page template :
<div id=”search”>
{search search_method=”post”}
</div>

Add a private area to your CMSMS

In order to add a private area with login to your CMSMS, you’ll have to download 3 extra modules :

  • FrontEndUsers (FEU) (will allow you to manage front-end users)
  • CustomContent (will allow you to display different contents to your different front-end users)
  • Captcha (to be used to add a Captcha test in your login form)
Copy the directories of these modules in your CMSMS/Modules directory. Then install the modules in Menu Extensions / Modules in the right order (FrontEndUsers, CustomContent, Captcha).

FrontEndUsers (FEU)

Menu Users & Groups / FrontEndUsers Management is now available. Open it and

  • go to the “User properties” tab to add properties to the users profile (a property called “name” with “text” type and a property called “email” with “Email address” type)
  • go to the “Groups” tab to create a group called “client_users” and custom your properties (name and email optional or required, for example, and tick email asked in lost username)
  • go to the “Users” tab and add a user for that group (do not forget to tick the new user’s group). Then you’ll have to fill in the properties you’ve defined earlier.

Let’s create the login page. Switch to Menu Content / Add new content and create the login page with the following content :

{FrontEndUsers}

Then associate it with your main template (options tab).

Your client CMSMS now displays a login page with login form and captcha.

Customize content

Once your user is loggedin, the client CMSMS should display extra menu items : the private pages. This is rather easy to achieve and is managed by the CustomContent Module. You’ll have to create a private template to ge associated with private page.

You can copy the main template of your site by clicking the “copy” button.

Instead of displaying

{content}

directly, type:

{if $ccuser->loggedin()}

{content}

{else}

Sorry, you need to login first

{/if}

Then create a private page in Menu Content / Pages and associate it with the new private template in the Options tab.

Your private content should now be displayed properly once logged in.

Menu manager

CMSMS provides a Menu Manager to edit or create new menu templates.

First check what menu is loaded in your default template. Open Menu Layout / Templates and look for div id=”menus” content. Let’s assume the menu template is cssmenu_ulshadow.tpl.

Open Menu Layout / Menu Manager. You’ll see that your cssmenu_ulshadow.tpl is not editable (but read only). Import it to your database by clicking the Import button. Give it a new name (test in my case).

Edit test menu template. You’ll see some smarty code. Look for the first foreach loop and add the following line just under it :

{$node->id}

Then, you have to change your default page template to take your “test” menu into account. Go to Menu Layout / Templates, edit the default template (NClearBlue in my case) and change the menu div content to :

{*menu template=’cssmenu_ulshadow.tpl’*}

{menu template=”test1″}

I’ve just commented the default menu template and added my new test template.

Go to the client site, refresh your page and you’ll be able to identify the menu ID of your private area (57 in my case).

Back to the Menu Layout / Menu Manager : I edit my test Menu, erase my {$node->id} and replace it by :

{if (($node->id == 57 and $ccuser->loggedin()) or $node->id <> 57)}

which checks if the page being displayed is the private area. If it is, the user needs to have logged in.

The last thing to do is to add a

{/if}

just before the {/foreach} at the bottom of the page.

Webliography :

Smarty.net

Smarty basics (tuto)

CMSMS Forge

CMSMS official Wiki

Recherche Fulltext sous MySql

Scripter un moteur de recherche nécessite :

  • un formulaire côté client pour soumettre des requêtes
  • des champs de table de base de données à l’intérieur desquels la recherche doit s’effectuer
  • des requêtes à la base de données pour rechercher la chaîne postée par le client

Je veux tout d’abord me pencher sur les caractéristiques de la base de données pour effectuer une telle recherche.

Auaparavant, j’utilisais des requêtes avec des sous-requêtes du type :

SELECT id,title,content from table_name WHERE title LIKE ‘%”.$search.”%’ OR content LIKE ‘%”.$search.”%’;

qui effectue une recherche de style expression régulière et peut causer des problèmes de ressources MySql (grand nombre de lignes parcourues, grand nombre de résultats,…).

Sous MySql, une recherche FULLTEXT peut solutionner ce problème de ressources et produire des résultats probants puisqu’elle effectue une recherche sur des données indexées, et donc pas directement dans la table. Ce type de recherche permet l’utilisation :

  • d’une liste de mots rejetés (mots que le serveurs exclut par défaut de la recherche)
  • de recherches booléennes (utilisant + ou -, par exemple, pour ajouter ou enlever des mots de la recherche)
  • d’un score de pertinence

Type de table qui permet l’indexation Fulltext

La recherche FULLTEXT est disponible avec les tables utilisant MyISAM comme moteur de stockage. Bien que le moteur de stockage InnoDB offre l’énorme avantage de pouvoir utiliser des transactions (gestion des erreurs par commit et rollback), la recherche par indexation Fulltext y est absente. Concentrons-nous donc sur les tables MyISAM.

Vous pouvez définir un index Fulltext sur un ou plusieurs champs de type CHAR, VARCHAR et TEXT. Comme mentionné dans la référence MySql reference sur le sujet, créer des données dans une table disposant d’un index Fulltext prendra plus de temps. Ainsi, si un nombre conséquent de données doit être traité avec un index Fulltext (et c’est dans ce cas qu’il sera le plus efficace), il vaut mieux d’abord insérer les données dans la table, puis créer l’index.

Vous pouvez créer un index sur les 2 champs “title” et “content” avec la requête suivante :

ALTER TABLE table_name ADD FULLTEXT index_name (‘title’ , ‘content’);

Une fois l’index Fulltext de la table défini, faites une recherche comme suit :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘”.$search.”‘);

Les champs de la sous-requête MATCH doivent être les mêmes que ceux définis dans l’index fulltext de la table, et vous pouvez créer un index Fulltext sur un champ ou plus.

Paramétrage de la recherche Fulltext

MySql est configuré avec un lot de paramètres par défaut :

  • les résultats sont classés par score de pertinence que MySql donne à chaque donnée trouvée
  • les mots de moins de 4 caractères seront ignorés
  • l’index Fulltext contient uniquement des mots entiers
  • liste des mots exclus d’une recherche Fulltext. Ce paramètre dépend de la langue d’installation de MySql
  • le score de chaque donnée trouvée dépendra du nombre d’occurences de ce mot dans la table. Si un mot apparaît dans plus de la moitié des lignes de la table, il sera ignoré

Le changement de ces paramètres par défaut ne sera possible que dans le cas où votre serveur Web MySql vous permet de les modifier.

Recherche en mode booléen

La sous-requête MATCH… AGAINST… permet la recherche booléenne :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘”.$search.”‘ IN BOOLEAN MODE);

MySql retournera toutes les lignes correspondantes même si plus de 50% des lignes contiennent le mot-clef.

Les recherches booléennes permettent en outre l’utilisation d’opérateurs comme + ou – pour forcer MySql à retourner les lignes contenant ‘works’ et cacher les lignes contenant ‘tables’ :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘+works -tables’ IN BOOLEAN MODE);

Vous pouvez aussi utiliser l’opérateur * comme joker en mode booléen pour rechercher une partie de mot :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘peopl*’ IN BOOLEAN MODE);

Webliography :

Référence sur la recherche FULLTEXT sous MySql

Référence sur la recherche FULLTEXT en mode Booléen sous MySql

MySql Fulltext search

Scripting an internal search engine requires :

  • a client form to submit requests
  • database fields to search posted keywords
  • database queries to match the form request

I want to focus on the database requirements to make such a search.

I used to build queries with sub-queries of the following type :

SELECT id,title,content from table_name WHERE title LIKE ‘%”.$search.”%’ OR content LIKE ‘%”.$search.”%’;

which performs a kind of regular expression search and can lead to MySql resource problems (large number of rows parsed, large number of results found,…).

Under MySql, a FULLTEXT search may solve this resource problem and achieve the search since it performs the search in indexed data. This feature allows

  • stopwords (words which will be excluded by the server itself)
  • boolean searches (using + or -, for example, to add to search or substract from search)
  • relevancy scoring

Type of table that allows fulltext indexing

FULLTEXT search is available in MyISAM tables. Though the InnoDB Storage Engine offers transactions (commit and rollback error handling), the fulltext search feature is absent. So, let’s stick to MyISAM tables.

You can define a FULLTEXT index on one or more table fields of types CHAR, VARCHAR and TEXT. As stated in MySql reference on the subject, creating rows in a table with fulltext index will take longer. So, if lots of rows need to be treated with a fulltext index (a fulltext index will be most efficient in this very case), it is better to first insert the rows, then create the index.

You can add a fulltext index on the 2 fields “title” and “content” with the following query :

ALTER TABLE table_name ADD FULLTEXT index_name (‘title’ , ‘content’);

Once the table has a fulltext index defined, the query can be the following :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘”.$search.”‘);

The fields in the MATCH statement have to be the same as defined in the table’s fulltext index, and you can define a fulltext index on one or more field.

Fulltext search parameters

MySql is configured with a set of automatic parameters :

  • the results are ordered by the relevancy score MySql gives to each row found
  • words with less than 4 characters will be ignored
  • the fulltext index contains complete words only
  • depending on the language MySql is set to, a list of stopwords will be automatically ignored
  • the score for each row depends on the number of occurences of that word in the table. If a word occurs in more than half the lines, it will be ignored

Changing these default parameters will only be possible if your MySql Web Server allows you to modify those parameters.

Boolean mode search

The MATCH… AGAINST… statement allows you to perform a Boolean search :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘”.$search.”‘ IN BOOLEAN MODE);

MySql will then return all rows matching the searched words even if more than 50% of the rows contain the keyword.

Boolean searches allow you to use operators like + or – to force MySql to return rows containing ‘works’ and hide rows containing ‘tables’ :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘+works -tables’ IN BOOLEAN MODE);

You can also use the * operator as a wildcard in Boolean mode to search part of a word :

SELECT id,title,content from table_name WHERE MATCH(title,content) AGAINST (‘peopl*’ IN BOOLEAN MODE);

Webliography :

MySql FULLTEXT search reference

MySql FULLTEXT search – Boolean mode reference

Sauvegardes & media de stockage

Le développeur à la recherche d’une stratégie de sauvegarde fiable trouvera plusieurs solutions abordables. Il n’est pas contraint d’en choisir une seule. La stratégie choisie gagnera en fiabilité si les sauvegardes sont faites sur des supports faisant appel à des technologies différentes et si le stockage des sauvegardes se trouve dans des bâtiments différents (imaginez un incendie détruisant tous vos backups stockés dans la même maison). Le développeur peut :

  • sauvegarder sur des supports amovibles (CD, DVD, Iomega REV)
  • sauvegarder sur des disques durs externes (DD)
  • sauvegarder sur un NAS (Network Attached Storage)

Supports amovibles

Les CDs et DVDs ne sont plus utilisés / conseillés pour des raisons écologiques et de fiablilité à long terme. De plus, copier des centaines de milliers de minuscules fichiers (< 1 ko) ne réussit pas toujours (selon le graveur, l’OS, la profondeur des répertoires,…). Le seul intérêt des backups sur support optique est leur prix : 0,09 euro / Go.

Je me suis donc tourné vers l’Iomega Rev (disques amovibles), la solution la plus chère : 800 euros pour un lecteur externe USB 2.0 et 5 disques 70 Go. Iomega a développé 3 modèles : 35 Go, 70 Go et 120 Go (un utilitaire fourni permet d’augmenter la capacité par compression). L’Iomega REV existe en drive interne (sata, atapi, scsi) ou externe (usb 2.0). La technologie REV est supposée être plus rapide que les sauvegardes sur bande (et ne coûte que la moitié du prix) et être plus efficace que les sauvegardes sur support optique (ce que je confirme). Mais elle reste la technologie de sauvegarde la plus chère que j’ai utilisée : 2,29 euros / 1 Go. Iomega annonce malgré tout une durée de vie de 30 ans pour ses disques REV. Une solution pour les développeurs en société, donc.

Les disques durs externes

Les disques durs (DD) sont financièrement intéressants (0,1 euro / Go) et sont donc une bonne solution de remplacement pour les sauvegardes optiques comme les DVDs. Je fais confiance à Western Digital en la matière.

Serveurs NAS (Network Attached Storage)

Vous pouvez améliorer votre stratégie de Backup en utilisant un NAS : un serveur avec 1 à n baies pour héberger 1 à n DDs. Une fois connecté à votre réseau, il est accessible de n’importe quelle station de travail, quel que soit l’OS. L’idée est de choisir un NAS avec la technologie RAID pour améliorer encore la stratégie de sauvegarde. J’utilise un NAS Synology à 2 baies configuré en RAID 1 (je n’ai accès qu’au premier des 2 disques de 750 Go, le second disque est utilisé pour le mirroring, ainsi, dès que quelque chose est écrit sur le premier disque, il l’est également sur le second). Un NAS 2 baies avec des disques d’1 To devrait coûter 500 euros, ce qui fait un tarif de 0,5 euro / 1 Go en RAID 1, ou 0,25 euro / 1 Go en RAID 0.

Le NAS connecté à votre réseau local est accessible par une interface utilisateur (souvent en AJAX). Il est livré avec un système d’exploitation LINUX et un firmware mis à jour en ligne.

Avant d’acheter un NAS, analysez vos besoins et choisissez le serveur adapté :

  • hardware : processeur, mémoire RAM, nombre de DD internes, interface DD externe (e-sata, usb 2.0), bruit, sortie audio USB/RCA,…
  • software : gestion RAID, applications fournies : File Station, Photo Station, Audio Station, Web Station, Surveillance Station,…

En cette fin d’année 2009, je conseillerais une solution QNAP (TS-439 Pro Turbo NAS) puisqu’il offre des fonctionnalités supplémentaires :

  • DD amovible à chaud
  • réduction de la production de bruit

Synchronisation de disques / répertoitres

Plutôt que copier L’entièreté d’un disque de données sur le NAS, j’utilise un logiciel de synchronisation qui va d’abord comparer les données du disque / répertoire source avec les données du disque de destination (NAS). Puis il synchronise de manière symétrique ou asymétrique. Syncback est un logiciel de synchronisation de données.

Il est également possible d’automatiser les backups avec des Crons sous Linux ou le Gestionnaire de Tâches Planifiées de Windows.

Webliography :

Tutoriel RAID

NAS Synology

NAS QNAP

Logiciel Syncback

Dossier NAS sur Clubic.com

Backups and Storage media

The developer in search of a reliable backup strategy can find several solutions. He does not have to choose only one. The chosen strategy may gain reliability if backups are done on devices based on different technologies and if storage takes place in different buildings (just imagine a fire destroys all your backups stored in the same house). You could choose between :

  • backing up on removable devices (CD, DVD, Iomega REV)
  • backing up on external hard disks (HD)
  • backing up on NAS (Network Attached Storage)

Removable devices

CDs and DVDs are not used anymore because they are not eco-friendly and are not reliable in the long term. Moreover, burning hundreds of thousands of tiny files (smaller than 1ko) on a data DVD may fail (depending on burning drive, OS, directory depth,…). The only interest in optical backups is their price : 0,09 euro / Go.

I then chose to turn to Iomega Rev removable disks, the most expensive solution (800 euros for drive + 5 disks). They have developed disks of 35 Go, 70 Go and 120 Go (a software included in the package allows compression to extend capacity). They have internal (sata, atapi, scsi) and external (usb 2.0) drives. REV technology is supposed to be faster than tape backups (for half the price) and more reliable than optical backups (which I confirm). But it stays the most expensive backup technology I have ever used : 2,29 euros / Go. “Pay the cost to be the boss” : Iomega announces a 30 years shelf-life for their disks.

External Hard disks

Hard Disks (HD) are easily affordable (0,1 euro / Go) and is therefore a replacement solution for optical backups like DVDs. I usually trust Western Digital

NAS (Network Attached Storage)

Your backup strategy can be improved using a NAS : a server with 1 to n bays that can hold 1 to n HDs. Connected to your network, it can be accessed from any OS part of your network. The idea is to choose a NAS with RAID so that you can improve the strategy some more. I use a two-bay Synology NAS configured with RAID 1 (I only have access to one of the 2 disks, the second one is used for mirroring, so, when I write something on disk 1, it is automatically written on disk 2). A 2-bay NAS with 1 To disks should costs 500 euros, which is 0,5 euro / 1 Go if you use RAID 1 or 0,25 euro / 1 Go when using RAID 0.

The NAS is connected to your local network and you access it through the network with a user interface. It is delivered with a Linux OS and a firmware you regularly update online.

Before buying, analyse the NAS server precisely :

  • hardware : CPU, memory, number of internal HD, external HD interface (e-sata, usb 2.0), noise level, USB/RCA audio output,…
  • software : RAID management, applications provided : File Station, Photo Station, Audio Station, Web Station, Surveillance Station,…

I would advise QNAP solutions (TS-439 Pro Turbo NAS) since they offer :

  • hot swappable HD
  • noise reduction

Synchronize Disks / Directories

In order not to copy a whole data disk to the NAS everytime I want to backup, I use a synchronizing software that first compares source disk data with destination (NAS) disk data. and synchronizes, updates the new or modified data. Syncback is a simple synchronizing tool software.

You may also automate backups using Cron under Linux or Scheduled Task Manager under Windows.

Webliography :

RAID tutorial

Synology NAS

QNAP NAS

Syncback software

PHP5 Afficher toutes les requêtes MySql d’une page Web

Le développeur qui veut rationaliser et/ou améliorer ses requêtes MySql peut trouver utile de faire afficher toutes les requêtes qu’une page Web a généré ainsi que le temps que le serveur a dédié à la production de cette page. Ceci n’est utile qu’en phase de développement. Une fois cette phase terminée, il faudra commenter (// ou /* */) ces quelques lignes de script pour gagner du temps de travail CPU.

Changements dans la classe MySql

Pour compter les requêtes MySql générées par une page Web et en afficher la liste, le développeur devrait ajouter une variable statique à sa classe MySql :

public static $queries = array();

Lors de l’initialisation de la classe, la variable $queries deviendra un tableau vide (array). A chaque fois qu’une requête est envoyée à la méthode d’exécution de la classe, cette requête sera ajoutée au tableau de la manière suivante :
array_push(self::$queries,$query);
Avant de détruire l’objet $mysql :
return count(self::$queries);
retournera le nombre de requêtes MySql générées par une page Web.
return self::$queries;
retournera le tableau contenant toutes les requêtes MySql de la page courante.

PHP script execution time

Pour vérifier combien de temps un script prend du début à la fin, utilisez microtime :
<?php
//haut du script PHP
$start_time = microtime(true); // >= PHP 5.0.0 donne ainsi le microtime avec décimales
//bas du script PHP
$end_time = microtime(true);
$total_time = $end_time – $start_time;
?>

PHP5 to watch over MySql queries

If the developer wants to write more efficient MySql queries, he may want to display all the queries a web page has generated and the time the whole script took to output its Html result. This is useful while developing. Once the website is launched, you may get rid of these script lines (just comment them with // or /* */) in order not to take extra CPU time.

Changes in the MySql class

In order to count MySql queries generated by a web page and display the list of them, he should add a static variable to his MySql Class :

public static $queries = array();

When initializing the class, the static variable $queries becomes an empty array. Each time a query will be sent to the class for execution, array $queries will be incremented this way :
array_push(self::$queries,$query);
Before destroying the $mysql object
return count(self::$queries);
will return the number of MySql queries generated by a Web page.
return self::$queries;
will return the array containing all MySql queries for the current web page.

PHP script execution time

In order to check how long it takes a script from start to end, just work with microtime :
<?php
//top of PHP script
$start_time = microtime(true); // >= PHP 5.0.0 gives microtime as a float
//bottom of PHP script
$end_time = microtime(true);
$total_time = $end_time – $start_time;
?>

Mysql5 – Requêtes multiples

Si vous voulez parcourir toutes les améliorations de MySql5, lisez les official relase notes.

L’objectif de ce post est d’écrire des requêtes MySql plus efficaces. Certains serveurs surveillent les ressources MySql qu’occupent une page Web. Si le nombre de requêtes pour une simple page Web est considéré comme trop gourmand en ressources, le développeur pourrait bien recevoir un avertissement (voire plus) de la part de l’hébergeur.

Lisez l’article “PHP5 : Afficher toutes les requêtes MySql d’une page Web” pour surveiller vos requêtes.

Ce sur quoi je veux me concentrer ici est une syntaxe qui a été introduite dans MySql 4.1 mais qui me semble trop peu utilisée :

INSERT INTO… ON DUPLICATE KEY UPDATE…

Mais parcourons tout d’abord la requête multiple pour l’effacement de données.

Effacer plusieurs lignes en une seule requête (DELETE)

Au lieu de générer des requêtes dans le but d’effacer des lignes d’une table :

$mysql = Mysql(); //initialisation de l’objet

$mysql->Connect(); //connexion à la BD

$array = array(1,2,4); //lignes à effacer

FOREACH($array as $value) {

$sql = “DELETE FROM table_name WHERE id=’$value’”;

$mysql->strictExecute($sql); //Exécution de la requête par l’objet $mysql

}

ce qui produira 3 requêtes dans cet exemple, vous pourriez faire la chose suivante :

$mysql = Mysql(); //initialisation de l’objet

$mysql->Connect(); //connexion à la BD

$array = array(1,2,4); //lignes à effacer

$sql = “”;

FOREACH($array as $value) {

IF($sql <> “”) $sql .= ” OR “;

$sql .= “id=’$value’”; //ajoute la chaîne id=’$value’ à $sql

}

$sql = “DELETE FROM table_name WHERE “.$sql; //construit la requête unique: DELETE FROM table_name WHERE id=’1′ OR id=’2′ OR id=’4′;

$mysql->strictExecute($sql); //Exécution de la requête par l’objet $mysql

ce qui produira une seule requête à la BD.

Je conseillerais même d’utiliser la sous-requête IN suivie de la condition :

$mysql = Mysql(); //initialisation de l’objet

$mysql->Connect(); //connexion à la DB

$array = array(1,2,4); //lignes à effacer

$sql = “”;

FOREACH($array as $value) {

IF($sql <> “”) $sql .= “, “;

$sql .= “‘$value’”; //ajoute la chaîne id=’$value’ à $sql

}

$sql = “DELETE FROM table_name WHERE id IN (“.$sql.”)”; //construit la requête unique : DELETE FROM table_name WHERE id IN (’1′, ’2′, ’4′);

$mysql->strictExecute($sql); //Exécution de la requête par l’objet $mysql

Insérer plusieurs lignes en une seule requête (INSERT TO)

Pour insérer de multiples lignes en une seule requête, utilisez la requête suivante :

$mysql = Mysql(); //initialisation de l’objet

$mysql->Connect(); //connexion à la BD

$books = array(0=>array(“title”=>”Mysql explained”,”n_pages”=>”350″),

1=>array(“title”=>”PHP explained”,”n_pages”=>”150″)

); //lignes à insérer

$sql = “”;

FOREACH($books as $value) {

IF($sql <> “”) $sql .= “, “;

$sql .= “(‘”.$value["title"].”‘,’”.$value["n_pages"].”‘)”;

}

$sql = “INSERT INTO table_name(title,n_pages) VALUES”.$sql;

$mysql->strictExecute($sql); //Exécution de la requête par l’objet $mysql

Mettre à jour plusieurs lignes (INSERT INTO… ON DUPLICATE KEY UPDATE)

Mettre à jour des lignes dans une table MySql peut se faire de la manière suivante :

$mysql = Mysql(); //initialisation de l’objet

$mysql->Connect(); //connexion à la BD

$books = array(0=>array(“id”=>1,”title”=>”Mysql explained”,”n_pages”=>”450″),

1=>array(“id”=>2,”title”=>”PHP explained”,”n_pages”=>”450″)

); //lignes à mettre à jour

$sql = “”;

FOREACH($books as $value) {

$sql .= “UPDATE table_name SET title=’”.$value["title"].”‘,n_pages=’”.$value["n_pages"].”‘ WHERE id=’”.$value["id"].”‘”;

$mysql->strictExecute($sql); //Exécution de la requête par l’objet $mysql

}

ce qui produira 2 requêtes.

Il est possible de n’en générer qu’une seule pour mettre à jour les 2 lignes par une requête INSERT INTO avec la syntaxe ON DUPLICATE KEY UPDATE. Dans le cas où la clef existe déjà (ici la valeur id = 1 et id = 2), MySql opérera un update. Si la clef n’existe pas, MySql fera un insert into classique :

$mysql = Mysql(); //initialisation de l’objet

$mysql->Connect(); //connexion à la BD

$books = array(0=>array(“id”=>1,”title”=>”Mysql explained”,”n_pages”=>”450″),

1=>array(“id”=>2,”title”=>”PHP explained”,”n_pages”=>”450″)

); //lignes à modifier

$sql = “”;

FOREACH($books as $value) {

IF($sql <> “”) $sql .= “,”;

$sql .= (‘”.$value["id"].”‘,’”.$value["title"].”‘,’”.$value["n_pages"].”‘);

}

$sql = “INSERT INTO table_name(id,title,n_pages) VALUES”.$sql.” ON DUPLICATE KEY UPDATEid=VALUES(id),title=VALUES(title),n_pages=VALUES(n_pages)”;

$mysql->strictExecute($sql); //Exécution de la requête par l’objet $mysql

Cette requête est tellement efficace qu’elle permet non seulement la mise à jour de plusieurs lignes, mais qu’elle inclut aussi l’insertion de nouvelles données, et ce, en une seule et même requête.

Webliographie :

MySql5 Release notes

MySql5 – Multiple queries

If you want to read more about MySql5 improvements, please read the official release notes.

The aim of this post is to write more efficient MySql queries. Some servers watch over the MySql resource a web page uses. If the number of requests for one single web page is considered too resource-hungry, the developer may receive a warning (or more) from the host!

Read article “PHP5 to watch over MySql queries” if you want PHP to display MySql queries generated in a specific Web page.

What I want to focus on here is a syntax that was introduced in MySql 4.1 but which is still rarely used :

INSERT INTO… ON DUPLICATE KEY UPDATE…

But first, let’s develop multiple queries for deletion.

Deleting several rows in one single query (DELETE)

Instead of dynamically producing queries in order to delete rows in a table :

$mysql = Mysql(); //initiate object

$mysql->Connect(); //initiate connection to DB

$array = array(1,2,4); //rows to be deleted

FOREACH($array as $value) {

$sql = “DELETE FROM table_name WHERE id=’$value’”;

$mysql->strictExecute($sql); //Execute query through $mysql object

}

which will produce 3 queries, you can do the following :

$mysql = Mysql(); //initiate object

$mysql->Connect(); //initiate connection to DB

$array = array(1,2,4); //rows to be deleted

$sql = “”;

FOREACH($array as $value) {

IF($sql <> “”) $sql .= ” OR “;

$sql .= “id=’$value’”; //adds string id=’$value’ to $sql

}

$sql = “DELETE FROM table_name WHERE “.$sql; //builds the single query : DELETE FROM table_name WHERE id=’1′ OR id=’2′ OR id=’4′;

$mysql->strictExecute($sql); //Execute query through $mysql object

which will produce one single query to DB.

I would even advise using the IN predicate :

$mysql = Mysql(); //initiate object

$mysql->Connect(); //initiate connection to DB

$array = array(1,2,4); //rows to be deleted

$sql = “”;

FOREACH($array as $value) {

IF($sql <> “”) $sql .= “, “;

$sql .= “‘$value’”; //adds string id=’$value’ to $sql

}

$sql = “DELETE FROM table_name WHERE id IN (“.$sql.”)”; //builds the single query : DELETE FROM table_name WHERE id IN (’1′, ’2′, ’4′);

$mysql->strictExecute($sql); //Execute query through $mysql object

Inserting several rows in one single query (INSERT TO)

In order to insert multiple rows in one query, do the following :

$mysql = Mysql(); //initiate object

$mysql->Connect(); //initiate connection to DB

$books = array( 0=>array(“title”=>”Mysql explained”,”n_pages”=>”350″),

1=>array(“title”=>”PHP explained”,”n_pages”=>”150″)

); //rows to be inserted

$sql = “”;

FOREACH($books as $value) {

IF($sql <> “”) $sql .= “, “;

$sql .= “(‘”.$value["title"].”‘,’”.$value["n_pages"].”‘)”;

}

$sql = “INSERT INTO table_name(title,n_pages) VALUES”.$sql;

$mysql->strictExecute($sql); //Execute query through $mysql object

Updating several rows in one single query (INSERT INTO… ON DUPLICATE KEY UPDATE)

Updating MySql rows can be done this way :

$mysql = Mysql(); //initiate object

$mysql->Connect(); //initiate connection to DB

$books = array( 0=>array(“id”=>1,”title”=>”Mysql explained”,”n_pages”=>”450″),

1=>array(“id”=>2,”title”=>”PHP explained”,”n_pages”=>”450″)

); //rows to be inserted

$sql = “”;

FOREACH($books as $value) {

$sql .= “UPDATE table_name SET title=’”.$value["title"].”‘,n_pages=’”.$value["n_pages"].”‘ WHERE id=’”.$value["id"].”‘”;

$mysql->strictExecute($sql); //Execute query through $mysql object

}

which will produce 2 queries.

The trick here is to generate an INSERT INTO query with the ON DUPLICATE KEY UPDATE syntax. In case the key already exists, only an update will take place. If the key doesn’t exist, Mysql will operate a classical insert into query :

$mysql = Mysql(); //initiate object

$mysql->Connect(); //initiate connection to DB

$books = array( 0=>array(“id”=>1,”title”=>”Mysql explained”,”n_pages”=>”450″),

1=>array(“id”=>2,”title”=>”PHP explained”,”n_pages”=>”450″)

); //rows to be inserted

$sql = “”;

FOREACH($books as $value) {

IF($sql <> “”) $sql .= “,”;

$sql .= (‘”.$value["id"].”‘,’”.$value["title"].”‘,’”.$value["n_pages"].”‘);

}

$sql = “INSERT INTO table_name(id,title,n_pages) VALUES”.$sql.” ON DUPLICATE KEY UPDATEid=VALUES(id),title=VALUES(title),n_pages=VALUES(n_pages)”;

$mysql->strictExecute($sql); //Execute query through $mysql object

Webliography :

MySql5 Release notes