as_reportool: PHP класс для формирования отчетов

as_reportool - это очередной вариант класса, предназначенного для формирования отчетов из данных, возвращенных SQL-запросом. Основная цель при написании класса была сделать максимально простым и прозрачным процесс подготовки описания отчета - в итоге он состоит в последовательном вызове нескольких методов (передача SQL запроса, списка выводимых и группирующих полей, дополнительных параметров) либо загрузка всех параметров из подготовленного XML файла с описанием отчета.
Программа распространаяется по лицензии GNU GPL, т.е. бесплатна для всех видов использования.

Установка скрипта стандартная - скопируйте файл as_reportool.php и вспомогательный as_dbutils.php в одну из папок своего сайта и используйте операторы "include(), require_once()" для подключения класса.

Использование as_reportool

require_once('as_reportool.php');
//...
$rep = new CReporTool();
Будем придерживаться "естественного" порядка вызова, однако он не столь важен. Главное - вызывать метод DrawReport() в последнюю очередь.

Шаг 1: передаем в класс текст SQL запроса, который вернет выводимые данные, отсортированные в нужном порядке, для чего вызываем метод SetQuery() (либо AddQuery(), чтобы добавить последовательно несколько запросов).

Шаг 2:, с помощью метода AddField() последовательно добавляем все "печатаемые" поля отчета. Учтите, что "идентификаторы" полей должны совпадать с "псевдо-именем" полей в данных, возвращаемых SQL запросом.
Например, если наш запрос содержит конструкцию типа "SELECT ... (field1+field2) `field1_field2`", для печати такого "вычисляемого" значения используем имя "field1_field2" :
$rep->AddField('field1_field2','сумма F1 и F2');

Шаг 3: Если нам нужны промежуточные итоги, передаем в класс имена "группирующих" полей (естественно, наш SQL запрос должен в этом случае группировать и сортировать записи в соответствии с этим набором полей), используем вызов метода AddGroupingField().
Группирующих полей может быть более чем одно, при наличии двух и более группирующих полей получатся многоуровневые под-итоги. Первое поле, указанное в методе AddGroupingField(), будет задавать "верхний" уровень в этой иерархии, а все последующие - уровни ниже предыдущего.


Шаг 4: вызываем DrawReport() для вывода отчета. При вызове DrawReport, во время цикла обработки данных (и печати строк отчета), когда меняется значение одного из группирующих полей, выводятся все накопленные под-итоги начиная с последнего уровня и до данного поля включительно.

Сформированный отчет представляет HTML код, который адекватно выглядит не только на экране, но и на распечатке. Отвечают за это подготовленные стили (CSS), которые выводятся перед началом самого отчета. Возможно, вам захочется задать свои стили форматирования вместо предложенных. В этом случае вывод CSS - блока можно отменить, вызвав метод SuppressCss(). Чтобы вывод был адекватным, в этом случае придется самостоятельно описать css-стили с идентификаторами: td.rep_ltrb, td.rep_lrb, td.rep_rb, td.num, td.cnt, td.newgroup.

CReporTool - список методов

  • CReporTool([$filename [, $outcharset]]) - конструктор может быть использован не только для создания пустого объекта, но и для загрузки описания отчета из подготовленого XML файла.
    Передайте его имя в первом параметре оператора new CReporTool(). Если параметр опущен, создается пустой объект CReporTool.
    Второй параметр используется, если вы загружаете описание из XML файла, и выходная кодировка (charset) отличается от UTF-8. В этом случае укажите имя конечного character set (например, 'windows-1251'), и все символьные значения (заголовки для полей, группировок, итогов) загруженные из XML, будут конвертированы из UTF-8. (Может, я не докопался до всех возможностей simpleXML, но пока получилось, что simpleXML функция simplexml_load_file не разбирает файлы в кодировке отличной от UTF-8 files, похоже, она вообще игнорирует атрибут encoding в заголовке XML. А потому приходится XML файл всегда готовить именно в UTF-8.)

    В первом параметре конструктора может быть не имя файла, а ассоциативный массив. В этом случае можно передать следующие параметры
    имя элементаназначение
    outname имя выходного сгенерированного файла
    outcharset Character set выходного файла
    backend URL/URI backend-скрипта, который будет использован для <a href...> ссылок в строках отчета
    bg_totals цвет фона итоговых строк
    bg_headings цвет фона строк заголовка отчета
    bg_newgroup цвет фона строк - заголовков новых групп в отчете
    bordercolor цвет рамки (border) отчета
    func_rowbg имя callback-функции, которую отчет будет вызывать для определения цвета фона каждой строки отчета (вы можете раскрашивать отчет в зависимости от значений в любом поле/полях строки отчета)
    groupnumbers передайте 1 если нужно делать нумерацию групп (в строках с заголовками групп включится вывод номера)
    rownumbers передайте 1 если нужно нумеровать строки отчета (в рядовых строках включится вывод номера)
  • LoadFromXml($filename [,$outcharset='']) метод загружает описание отчета из XML файла. Собственно, этот метод и вызывается в конструкторе, когда мы вызываем его с непустым параметром. Первый параметр - это имя файла, второй - кодировка, (см. описание конструктора выше). Структура XML файла должна соответствовать приведенному ниже примеру, из которого практически все понятно.
    ВНИМАНИЕ: если вы планируете использовать этот метод, удостоверьтесь в том, что используете на хостинге PHP 5-ой версии с включенной поддержкой simpleXML.

    Пример XML файла
    
    <?xml version="1.0" encoding="UTF-8"?>
    <report_def version="1.00">
      <query>SELECT c.categoryid, b.animalid, a.nickname,a.gender,a.birth, a.weight
       FROM big_zoo a, animals b, animal_categories c
       WHERE a.animalid=b.animalid AND b.category=c.categoryid ORDER BY c.categoryid, b.animalid
      </query>
      <headings><![CDATA[
    <tr><td colspan="8" class="rep_ltrb">This is additional headings to print before default columns headings</td></tr>
    ]]></headings>
      <nodefaultheadings value="1" />
      <summary title="Summary totals (%rowcount%)" />
      <delimiters decimal="," thousand=" " />
      <grpfield name="categoryid" fconv="GetAnymalCategoryName" title="Animal category " totaltitle="Totals for category" />
      <grpfield name="animalid" fconv="GetAnymalClassName" title="Animal class" totaltitle="Totals for class" />
    
      <field name="nickname" title="Nick" fconv="" format="" />
      <field name="gender" title="Gender" fconv="DecodeGender" />
      <field name="weight" title="Weight, kg" summable="1" format="i" />
    
    </report_def>
    
    

  • query - тег задает SQL запрос, аналогично вызову SetQuery().
    headings - задание дополнительных заголовков (см. метод SetHeadings() );
    nodefaultheadings - этот тег с атрибутом value="1" "прячет" авто-форируемые заголовки для колонок в шапке отчета (см. с непустым вторым параметром: SetHeadings($new_headings,1);
    grpfield - задание группирующего поля (см. AddGroupingField() ),
    field - добавление печатаемой колонки с полем (вызов AddField() );
    summary - включение вывода финальных итогов по отчету, с заданием строки титула (вызов SetSummary() );
    delimiters - переключение символов разделителей в выводе числовых значений (см. SetNumberDelimiters($decimal,$thousand) ).

  • AddGroupingField($fldid,$fconv='',$title='',$totaltitle='') добавляет группирующее поле, при смене значения которого будут печататься строки с под-итогами. Как уже говорилось, можно задавать более одного группирующего поля (последовательным вызовом метода).


  • AddField($fldid,$fldtitle='',$summable=0,$fconv='', $format='') добавляет в отчет колонку с полем.
    Параметры:


  • setQuery($query) - задает SQL запрос, который получит все данные для печати отчета.

    AddQuery($query) - добавляет (еще один) SQL запрос к отчету. CReporTool будет вызывать все переданные запросы по порядку, и выведет в отчет все полученные записи. Учтите, метод setQuery обнуляет накопленный список и задает только один запрос.

  • SetHeadings($par, $no_defaultheader=0) : по умолчанию для отчета формируется простая "шапка", состоящая из наименований полей, переданных в методе AddField() (параметр $fldtitle).
    Чтобы вывести что-то более сложное, подготовьте свой HTML код шапки и передайте его в класс с помощью метода SetHeadings(). Кроме самого кода (первый параметр) можно также передать непустой (0, true) второй параметр, отключающий генерацию стандартного кода шапки. HTML код должен представлять набор табличных строк, т.е. начинаться тегом <tr> и заканчиваться </tr>. Он выводится перед стандартным (сгенерированным) кодом заголовков.


  • SetSummary($title) : метод вызывается, если в конце отчета нужно распечатать итоговую строку. Можно передать свой текст заголовка в параметре $title. Чтобы в этом тексте вывести общее число строк данных, попавших в отчет, включите в строку макрос %rowcount%.


  • AddHeadingGroup($groupTitle, $field, $fcount=2) - формирует группировку (объединение) заголовков отдельных полей в одну группу. Например, в заголовках есть колонки 1-й квартал, 2-й квартал и т.д.. И мы хотим, чтобы все они были "накрыты" сверху объединяющим заголовком "Данные по кварталам". Если поле "1-ый квартал" имдет десятым по счету, то мы должны вызвать метод следующим образом:

    $report->AddHeadingGroup('Данные по кварталам', 10,4); // объединяем 4 заголовка начиная с 10-го)

    setReportData($data) - если вместо передачи SQL запросов вы сами хотите сформировать массив данных для отчета, используйте вызов этого метода. Это полезно в случаях, когда SQL запросы не могут выдать готового к печати набора данных, или у вас вообще нет MySQL сервера, и все данные формируются из других источников.

  • SetNumberDelimiters($dec, $tho) заменяет стандартные символы разделителей для дробной части и тысяч, используемые при вызовах number_format, когда форматируются числовые значения полей (полей с типами формата i,money).
    Стандартные символы разделителей - "," для тысяч и "." for дробной части.


  • SetFontStyles($par) устанавливает новый стиль для всего выводимого отчета и должен состоять из css атрибутов. Например, чтобы вывести отчет шрифтом "verdana" с размером 3mm, передайте строку :
    $rep->SetFontStyles("font-family: Verdana; font-size:3mm;");


  • DrawReport($title='') финальный метод, генерирующий и выводящий HTML код отчета. Последовательно отрабатываются строки данных, возвращаемые переданным SQL запросом, подсчитываются промежуточные (и финальные) итоги.
    Необязательный параметр $title передает титульную строку, выводимую в начале отчета (перед "шапкой").
  • Пример данных для демонстрации работы класса.

    Для демонстрации нам понадобятся три таблицы в нашей БД. Это будет описание гипотетического зоопарка. Первая таблица будет содержать список "базовых" категорий животных (морские, ...) Вторая - список типов животных (слоны, гориллы и т.д.), и третья - список обитателей зоопарка.
    Для генерации данных приготовлен sql-файл, который можно выполнить в консоли mySql командой source sampledata.sql

    Полетипописание
    таблица animal_categories
    categoryidINTunique category's ID
    categorynameVARCHARcategory name
    таблица animals
    animalidINTunique animal type's ID
    categoryINTcategory of the animal
    animalnameVARCHARanimal's name
    таблица big_zoo - all animals living in the zoo
    itemidINTliving creature's ID
    animalidINTwhat kind of animal is it (ID)
    nicknameVARCHARnick name given to this creature
    birthDATEbirth date
    genderCHAR(1)gender(m for male, f for female)
    weightINTcreature's weight


    Будем выводить отчет по всем особям, живущим в зоопарке, с промежуточными итогами и строкой общих итогов.
    $rep = new CReporTool();
    
    $rep->SetQuery("SELECT c.categoryid, b.animalid, a.nickname,a.gender,a.birth,a.weight
       FROM big_zoo a, animals b, animal_categories c
       WHERE a.animalid=b.animalid AND b.category=c.categoryid ORDER BY c.categoryid, b.animalid");
    
    $rep->AddGroupingField('categoryid','GetAnymalCategoryName','Animal category ','Totals for category');
    
    $rep->AddGroupingField('animalid','GetAnymalClassName','Animal class ','Totals for class');
    
    $rep->AddField('nickname','Nick');
    
    $rep->AddField('gender','Gender',0,'DecodeGender'); // DecodeGender() will show 'male' for 'm' and female for 'f' value.
    $rep->AddField('birth','Birth date',0,'DateToChar'); // your function DateToChar converts DATE value to be more readable
    $rep->AddField('weight','Weight, kg',1,'','i'); // this field is summable and will be printed right-aligned and number_format()ted
    $rep->DrawReport('Report: All animals in zoo');
    

    Возможно, Вы решите воспользоваться возможностями базы для формирования строк под-итогов (например, используя GROUP BY WITH ROLLUP в MySQL) - так вот, делать это не стоит, т.к. CReporTool считает итоги самостоятельно и не умеет распознавать итоговые строки в рекордсете.

    Примерный вид отчета приводится ниже:

    Report: All animals in zoo

    NickGenderBirth dateWeight, kg
    Animal category Sea animals
      class : whales
     Whale-Boy male 06/22/1997 7,000
     Whale-Girl female 08/01/2000 6,500
    Totals for whales13,500
      class : sharks
     BloodyJaws male 09/15/2001 1,500
     HungryMary female 11/10/2002 1,200
    Totals for sharks2,700
      class : dolphins
     BigMartha female 05/20/1998 3,000
     BigBob male 01/12/1996 3,500
    Totals for dolphins6,500
    Totals for category Sea animals22,700
    Animal category Jungle animals
      class : elephants
     LazyTom male 10/23/2004 5,000
     MightyJack male 06/16/2003 5,300
     Samantha-Shy female 04/24/2004 4,200
    Totals for elephants14,500
      class : gorillas
     Gorilla-Mom female 08/23/2001 150
     Gorilla-Son male 04/15/2008 40
     Gorilla-Dad male 02/05/2000 180
    Totals for gorillas370
    Totals for category Jungle animals14,870
    Summary all animals (12) 37,570


    В дистрибутиве должен присутствовать пример скрипта, формирующий наш отчет.

    Change log (История изменений)

    1.23 (09/30/2013)
    1.00.002 (11/26/2008)
    Первый релиз
    Copyright © Alexander Selifonov, www.selifan.ru, distrubuted under GPL