CiviCRM Developer Guide

Writing custom reports

This chapter is a brief introduction to creating a new report in the CiviReport system. You can use the same programming techniques to extend an existing report template. These tasks call for a strong grasp of PHP. More details are available in the wiki page on customizing reports:
http://wiki.civicrm.org/confluence/display/CRMDOC/CiviReport+structure+and+customization
.

Creating or changing a report template is the wrong choice when ...

If you want a batch action on an existing result set. CiviReport does not support most of the batch actions. If your main purpose is to print or send an email to a list of contacts that meet specific criteria, write a custom search.

Report specification

Before starting on a custom report, fill out the form on the CiviCRM wiki for report specifications. Add your specification there and ask for comments in the forum or on IRC.

Creating a custom template

This section creates a custom report listing all contacts' Display Name, First Name and Last Name. Note that we'll be talking about both Smarty templates (.tpl files) and PHP report templates--be careful not to confuse the two.
Replacing an existing report with your own version is not recommended because you may need the original too, and because an upgrade of CiviCRM will overwrite your changes with the CiviCRM version. In this section, therefore, we'll create a new Smarty template and a new PHP template for the report. You can also copy a template and report to a new location and edit them to create your custom report.

All reports are located under CRM/Report/Form/ and grouped by component.

Create a new Smarty template and a new PHP template for the report. Because this example creates a report about contacts, we'll create a new report template named Contact.php and a Smarty template named Contact.tpl, both in a custom directory (the Contact.php in the custom directory for php scripts, the Contact.tpl in the custom directory for templates). The paths will look like:

PHP_CUSTOM_PATH/CRM/Report/Form/Contact/Contact.php

TPL_CUSTOM_PATH/CRM/Report/Form/Contact/Contact.tpl
 

Add a base class named CRM_Report_Form_Contact_Contact in Contact.php, Your class must inherit the form class used for the report framework. So Contact.php looks like:

<?php
require_once 'CRM/Report/Form.php';
class CRM_Report_Form_Contact_Contact extends CRM_Report_Form {
}

Your Smarty template file must include the report framework template. Thus, Contact.tpl is:

{include file="CRM/Report/Form.tpl"}

Register your report template in CiviCRM. Go to: Administer -> CiviReport -> Register Report. Enter the details shown in the following screenshot.
Reports_register
In a constructor, create an array to hold a contact, which in turn hold an array specifying two fields you want to display.

function __construct( ) {
    $this->_columns = array( 'civicrm_contact' =>
    array(
        'dao' => 'CRM_Contact_DAO_Contact',
        'fields' => array( 'first_name' => array( 'title' => ts( 'First Name' ) ), 'last_name' => array( 'title' => ts( 'Last Name' ) ),
    ) ) );
parent::__construct( );
}
Build the display by issuing a SELECT against the database. You can create column headers and fill in each field in the display with the results returned by the SELECT.
    function preProcess( ) {
        parent::preProcess( );
    }
// build select query based on display columns selected
    function select( ) {
        $select = $this->_columnHeaders = array( );
        foreach ( $this->_columns as $tableName => $table ) {
            if ( array_key_exists('fields', $table) ) {
                foreach ( $table['fields'] as $fieldName => $field ) {
                    if ( CRM_Utils_Array::value( 'required', $field ) ||
                    CRM_Utils_Array::value( $fieldName, $this->_params['fields'] )) {                       $select[] = "{$field['dbAlias']} as {$tableName}_{$fieldName}";           // initializing columns as well
           $this->_columnHeaders["{$tableName}_{$fieldName}"]['type']  =
               CRM_Utils_Array::value( 'type', $field );
           $this->_columnHeaders["{$tableName}_{$fieldName}"]['title'] = $field['title'];
                    }
                }
            }
        }
        $this->_select = "SELECT " . implode( ', ', $select ) . " ";
    }
    function from( ) {
        $this->_from = "FROM civicrm_contact {$this->_aliases['civicrm_contact']}";
    }

Your custom report template is ready. Press the Preview button to see the results.Contact Report
After you've installed and tested your report, add it to the CiviCRM wiki as a patch to the project so that it can be used by others in the community.

An example of creating a custom report extension

Custom reports are made of a PHP class and the template. Once we have that, we can go ahead and package it.

You need to prepare the info file as described in the Extensions Framework Chapter.

Sample info.xml file for custom report

<?xml version="1.0" encoding="UTF-8" ?>
<extension key="org.civicrm.report.grant" type="report">
<callback>Grant</callback>
<name>Grant Report</name>
<description>Grant Report allows you to see the summary of grants that
have been admitted to your consitutents by your organisation. </description>
<url>http://civicrm.org</url>
<license>AGPL</license>
<maintainer>CiviCRM Core Team &lt;noreply@civicrm.org&gt;</maintainer>
<releaseDate>2010-09-01</releaseDate>
<version>1.0</version>
<develStage>stable</develStage>
<compatibility><ver>3.3</ver></compatibility>
<comments>For support, please contact project team on the forums. (http://forum.civicrm.org)</comments> </extension>
⁞Then once you have the info file, you can start putting the extension package together. We'll choose " org.civicrm.report.grant " to be the unique identifier of the extension, so we need to give the same name to the directory that will contain our extension. Once you've created that, put the info.xml file in it.
  • Create the directory org.civicrm.report.grant and add the info file
  • Put the report PHP class file in the directory. Remember to name it the same way as the callback attribute in your info.xml, also don't forget to follow the convention in class naming ("Extension_<Type>_<key>_<callback>" - Extension_Report_org_civicrm_report_grant_Grant in this case).
  • Create org.civicrm.report.grant/templates directory and put your report's template file there

Report needs some additional information compared with custom search and we need to define it somewhere. Therefore, we'll create another XML file called report.xml. That will simply provide the extension framework with all the necessary information needed for adding it to the system. Here's how it looks:

<?xml version="1.0" encoding="UTF-8" ?>
<report_template key="org.civicrm.report.grant">
<report_url>grant/summary</report_url>
<component>CiviGrant</component>
</report_template>

Attributes explained:

  • report_url  - the url under which this report will be server within CiviCRM. It's relative to CiviCRM's base URL.
  • component - the name of the component this report works with. Currently accepted options are: Contact, CiviEvent, CiviContribute, CiviMember, CiviMail, CiviGrant, CiviPledge, CiviCase. If unknown value is put here, "Contact" is assumed.

We should have following directory structure containing following files:

org.civicrm.report.grant/
|-- Grant.php
|-- info.xml
|-- report_template.xml
`-- templates
`-- Grant.tpl

Looks similar to what you have on your sandbox? Then the last thing required is to put it in the archive - "zip" it and call the file after the key defined at the beginning: org.civicrm.report.grant.zip. Congratulations, you have now packaged your first custom report.