Créer un module pour drupal 7 + Views 3

Source: OReilly Programmers Guide to Drupal p81

Le vocabulaire "Views 7.x-3.x"

handler -> classe qui s'occupe de l'affichage des chps, mais aussi de leur tri, filtre, filtre contextuel et relations
plugin -> classe qui s'occupe de l'affichage du module Views et d'autres classes qui gèrent les fonctions de base de Views

Comment Views utilse handlers et plugin pour construire un output ? En (très) gros:

  1. Views prend les relations, filtres et des des champs, crée et exe une requete
  2. si la vue utilise des champs, chaque chp est pris avec son handler d'affichage de chp
  3. si la vue utilise "row style plugin" alors chaque ligne du résultat de la requete est pris avec un "row style plugin"
  4. les lignes formattées sont envoyées au plugin style qui combine les lignes vers l'output. Views a des plugin pour les formats "tableau html", listes non ordonnées... chaque plugin style est compatible avec certains sous ensembles de plugins de style ligne.
  5. puis ca part au plugin display (ex de plugin display: les vues standards de Views Page, Block, Feed)

Configurer son module pour travailler avec Views 3

Pour que Views voit son nouveau module, implémenter hook_views_api() dans monmodule.module - ce hook aura besoin d'un dossier /modules/monmodule/views
Créer ce dossier /modules/monmodule/views , ainsi qu'un sous dossier pour les fichiers de templates: /modules/monmodule/views/templates.

function monmodule_views_api() {
return array(
// la version de the Views API utilisée. Si Views 7.x-3.x, prendre 3.
'api' => 3,
// Where your additional Views directory is.
'path' => drupal_get_path('module', 'monmodule') . '/views',
// Where Views-related theme templates are located.
'template path' => drupal_get_path('module', 'monmodule') .
'/views/templates',
);
}

monmodule.info devrait avoir la ligne dependencies[] = views

Penser à vider le cache de Views qd on implémente un nouvel hook de views, ou modif... Ou désactiver le cache de Views (advanced settings de Views).

Fournir une data source

Les données gérées par notre module doivent etre accessible à Views.
Si ces données sont gérées dans des entitées ou champs + module "entity API" pour définir une entitée perso ou via un chp dans une entitée existante alors facile.
Si par contre son module stocke ses données dans une table perso, l'intégrer en définissant une nouvelle data source pour views (aussi appellée table de base):

Créer un fichier nommé monmodule.views.inc

hook_views_data() retourne un tableau associatif de tableaux avec nom de la table et infos sur table et chps. Ex:
// The table name from hook_schema() is 'api_documentation'.
$data['api_documentation'] = array(         // Information about the table itself.
  'table' => array(
  'group' => t('API documentation'),
               // Group used for the fields in this table.
  'base' => array(
  'field' => 'did',                        
// The primary key of this table.
  'title' => t('API documentation'),             // The label shown when selecting this table in Views.
  'help' => t('API documentation objects'),
  'weight' => 20,
),
),
'did' => array(
'title' => t('Documentation ID'), // Info about the primary key field. It cannot be used for display, filter, or sorting.

'relationship' => array(
                 // Relationship with the Comment table.
'base' => 'comment',
'base field' => 'nid',       // Field in the comment table that corresponds to this field.
'handler' => 'views_handler_relationship',
'label' => t('Comments'),
'title' => t('All comments'),
'help' => t('All comments on the documentation object'),
),
),

'object_name' => array(                               // Another field.

'title' => t('Object name'),
'help' => t('Name of this object'),
'field' => array(
             // How this field can be used for display. This uses a custom field display handler class.
'handler' => 'api_views_handler_field_api_linkable',
'click sortable' => TRUE,
),

'sort' => array(           // Sorting is handled by the generic Views sort handler.
'handler' => 'views_handler_sort',
'click sortable' => TRUE,
),
'filter' => array(        // Filtering is handled by the generic Views string filter handler.
'handler' => 'views_handler_filter_string',
),
'argument' => array(      // Contextual filtering is handled by the generic Views string argument handler.
'handler' => 'views_handler_argument_string',
),
),
);

Soit on utilise les classes handler standard de views (voir sites/all/modules/views/handlers) soit on crée sa classe handler.

créer sa propre classe handler (pour views)

  1. Créer dossier /modules/monmodule/views/handlers (idem implémentation de hook_views_api())
  2. dans ce dossier handlers créer un fichier include nommé monmodule_views_handler_field_myfield.inc si on nomme sa classe  monmodule_views_handler_field_myfield
  3. dans ce fichier, étendre une classe standard de views de meme type (chp, filter...) et overrider les bonnees méthodes pour définir ses actions de notre nouvelle classe handler.
    Ex cas handler de chps: étendre la classe views_handler_field, et overrider la méthode render() (et probablement aussi option_definition() et options_form())
  4. ajouter au .info    files[] = views/handlers/monmodule_views_handler_field_myfield.inc

Champs et relations dans une data source existante - p 86

en implémentant hook_views_data_alter() dans /modules/monmodule/views/mymodule.views.inc

Ce hook est pratique dans 2 cas de base: ajout relation sur table existante ; ajout d'une jointure automatique existante (ex node_comment_statistics)

function api_views_data_alter(&$data) {
                                             // Add a relationship to the Comment table.
$data['comment']['did'] = array(
'title' => t('Documentation ID'),
'help' => t('The ID of the documentation object the comment is a reply to.'),
'relationship' => array(
                        // Table to join to.
'base' => 'api_documentation',
'base field' => 'did',               // Field in that table to join with.
'field' => 'nid',                    // Field in the comment table to join with.
'handler' => 'views_handler_relationship',
'label' => t('API documentation object'),
'title' => t('API documentation object'),
'help' => t('The ID of the documentation object the comment is a reply to.'),
),
);
// Add an automatic join between the comment statistics table and the API documentation table.
$data['node_comment_statistics']['table']['join']['api_documentation'] =
array(
'type' => 'INNER',                 // Use an inner join.
'left_field' => 'did',  // Field to join on in the API documentation table.
'field' => 'nid',      // Field to join on in the comment statistics table.
);
}

Fournir à Views un plugin display (ex: nouveau style ou row style)

1. avec hook_views_plugins() - cf hook_views_api() pour le lieu, mymodule.views.inc

function mymodule_views_plugins() {
return array(
'style' => array(     // Overall style plugins
'mymodule_mystyle' => array(
            // First style plugin--machine name is the array key.
'title' => t('My module my style'),  // Information about this plugin.
'help' => t('Longer description goes here'),
'handler' => 'mymodule_views_plugin_style_mystyle',
// The class for this plugin and where to find it.
'path' => drupal_get_path('module', 'mymodule') . '/views/plugins',
'uses row plugin' => TRUE,  // Some settings.
'uses fields' => TRUE,
),
// Additional style plugins go here.
),
// Row style plugins.
'row' => array(
// First row style plugin -- machine name is the array key.
'mymodule_myrowstyle' => array(
// Information about this plugin.
'title' => t('My module my row style'),
'help' => t('Longer description goes here'),
// The class for this plugin and where to find it.
'handler' => 'mymodule_views_plugin_row_myrowstyle',
'path' => drupal_get_path('module', 'mymodule') . '/views/plugins',
// Some settings.
'uses fields' => TRUE,
),
// Additional row style plugins go here.
),
);
}

2. Créer un fichier .inc pour chaque style ou plugin "row style".
Par ex on a déclaré une classe nommée mymodule_views_plugin_style_mystyle, alors on crée un fichier nommé mymodule_views_plugin_style_mystyle.inc qui doit etre au meme endroit que dans hook_views_plugins => monmodule/views ou monmodule/views/plugins

3. le .info doit mentionner chaque classe (avec fichier include) avec une ligne comme files[] = views/plugins/mymodule_views_plugin_style_mystyle.inc

4. dans chaque classe (avec inc) déclarer sa nouvelle classe de plugin, qui étend soit views_plugin_style, views_plugin_row, ou toute autre sous-classe de ces 2 dernières

5. Utiliser hook_theme pour le theme et preprocess de notre plugin (mymodule-mystyle.tpl.php ou mymodule-myrowstyle.tpl.php)

Autre modules "Views plugin"
http://drupal.org/project/views_data_export
http://drupal.org/project/calendar
http://drupal.org/project/views_slideshow

Fournir une vue par défaut

Créer une views (UI) et l'exporter (commence par $view = new view;).
Puis, pour avoir la vue en disabled par défaut, chercher la ligne proche du haut $view->disabled = FALSE; et la modifier en $view->disabled = TRUE;
Dans mymodule.views_default.inc utiliser hook_views_default_views()
Et coller le code PHP exporté de views dans hook_views_default_views() :

function mymodule_views_default_views() {
$views = array();   
// We'll return this array at the end.
$view = new view;     // Exported
// ... rest Exported ... end export code

$views[$view->name] = $view;             
// Add this view to the return array.
// You can add additional exported views here.
return $views;
}