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='') добавляет группирующее поле,
при смене значения которого будут печататься строки с под-итогами.
- $fldid - имя поля (или псевдо-имя для колонки данных,
- $fconv имя функции, используемой для преобразования значения поля во что-то более информативное (для вывода на печать).
Например, если значение - это числовой ID подразделения, указанная функция должна возвращать название подразделения по его номеру (ID).
- $title титул, или наименование для поля, из которого будет формироваться блок заголовков (шапка отчета)
- $totaltitle текст, выводимый в строке итогов слева от итоговых значений. Макрос "%name%" в этой строке будет заменен
на значение группирующего поля (подразделения, например),
т.е. если задать "Итого для %name%", строка итогов будет выглядеть как "Итого для (название здесь)"
Как уже говорилось, можно задавать более одного группирующего поля (последовательным вызовом метода).
AddField($fldid,$fldtitle='',$summable=0,$fconv='', $format='') добавляет в отчет колонку с полем.
Параметры:
- $fldid - имя поля (или псевдо-имя как представлено в SQL запросе)
- $fldtitle наименование для поля, (используется в заголовках шапки отчета)
- $summable : если true (или любое непустое значение), для данного поля будут подсчитываться и выводиться
под-итоги
- $fconv - необязательное имя функции преобразования значения поля в его "печатное" представление.
Если для формирования "печатной версии" значения поля нужны другие поля в данной записи (например, код валюты, если данное поле - стоимость или премия,
которую надо вывести с кодом USD,EUR и т.д.), то в своей пользовательской функции можно использовать синтаксис с двумя параметрами:
во второй параметр передается вся строка данных в виде ассоц.массива.
- $format - необязательный идентификатор формата поля. Поддерживаются следующие форматы:
format | Что означает |
c | "Centered" - значение центруется в ячейке |
r | "Right" - значение выравнивается вправо |
money | значение преобразуется функцией number_format() к "денежному" виду (NNN NNN.00)
и выравнивается вправо |
i | "Integer" - значение форматируется number_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 |
categoryid | INT | unique category's ID |
categoryname | VARCHAR | category name |
таблица animals |
animalid | INT | unique animal type's ID |
category | INT | category of the animal |
animalname | VARCHAR | animal's name |
таблица big_zoo - all animals living in the zoo |
itemid | INT | living creature's ID |
animalid | INT | what kind of animal is it (ID) |
nickname | VARCHAR | nick name given to this creature |
birth | DATE | birth date |
gender | CHAR(1) | gender(m for male, f for female) |
weight | INT | creature'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
Nick | Gender | Birth date | Weight, 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 whales | 13,500 |
class : sharks |
BloodyJaws | male | 09/15/2001 | 1,500 |
HungryMary | female | 11/10/2002 | 1,200 |
Totals for sharks | 2,700 |
class : dolphins |
BigMartha | female | 05/20/1998 | 3,000 |
BigBob | male | 01/12/1996 | 3,500 |
Totals for dolphins | 6,500 |
Totals for category Sea animals | 22,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 elephants | 14,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 gorillas | 370 |
Totals for category Jungle animals | 14,870 |
Summary all animals (12) | 37,570 |
В дистрибутиве должен присутствовать пример скрипта, формирующий наш отчет.
Change log (История изменений)
1.23 (09/30/2013)
- добавлена возможность выводить номера групп (в конструкторе параметр "groupnumbers")
- новый метод AddHeadingGroup() - поддержка объединения колонок в заголовке отчета
- новый метод setReportData()
- новый метод AddQuery()
1.00.002 (11/26/2008)
Первый релиз