Статьи

Создание электронных таблиц с PHP и PEAR

В предыдущей статье Начало работы с PEAR вы получили и запустили менеджер пакетов PEAR. Теперь пришло время эффективно использовать PEAR с PEAR :: Spreadsheet_Excel_Writer , библиотекой для создания электронных таблиц Excel.

Вот что мы рассмотрим сегодня:

  • Представляем PEAR :: Spreadsheet_Excel_Writer
  • Найти свой путь: знакомство с API
  • Добавление форматирования ячеек
  • Добавление функций Excel: C1 + D1 = 2!

Имейте в виду, что я предполагаю, что у вас есть элементарные знания Excel, но ничего серьезного. Вам даже не нужно иметь копию — все приведенные здесь примеры одинаково хорошо работают с OpenOffice Calc .

Представляем PEAR :: Spreadsheet_Excel_Writer

Посмотрим правде в глаза — хотя вы и я, возможно, озабочены разметкой XML, просмотром вкладок, редакторами, такими как Emacs и VI, и минимизацией использования системных ресурсов, остальной мир, использующий компьютеры, с удовольствием уходит от подобных Microsoft Office , И хотя они могут быть слегка впечатлены удивительными вещами, которые вы можете сделать с таблицей HTML, когда дело доходит до работы с числами, Excel в значительной степени является стандартом.

Более того, Excel широко используется теми, кто занимается финансами и деньгами. Другими словами, бухгалтерия, которая не смогла оплатить ваш счет вовремя, вероятно, использует его. Сделайте жизнь бухгалтеров проще, и они могут вернуть одолжение …

Разве не было бы замечательно, если бы вы могли предоставить своим клиентам доступ к загружаемым числовым данным в форме электронной таблицы Excel? Хорошей новостью является то, что вы можете с PEAR :: Spreadsheet_Excel_Writer .

«Невозможно!» ты плачешь. «Excel использует какой-то проприетарный формат файлов Microsoft. Это невозможно!»

Да, оно может. Spreadsheet_Excel_Writer генерирует «настоящую вещь», в комплекте с функциями Excel, форматированием и всем. Нет, мы не говорим о создании файлов, разделенных запятыми, или использовании расширения COM (или любого другого расширения в этом отношении). Это написано на чистом PHP и будет работать так же хорошо с веб-сервера на основе UNIX, как и с сервера на основе Windows — и вам не нужно будет ошибаться у вашего хостинг-провайдера. Короче говоря, PEAR :: Spreadsheet_Excel_Writer, с дополнительной магией из PEAR :: OLE , «понимает» форматы файлов Microsoft Excel.

Давайте потратим немного времени на то , чтобы надеть наши шляпы Ксавье Ногеру , который проделал потрясающую работу по переносу всего этого на PHP, с помощью Мики Тууполы для Spreadsheet_Excel_Writer.

Теперь, без лишних слов и вооруженных полным знанием менеджера пакетов PEAR, который вы успешно установили в прошлом месяце (верно?), Давайте начнем с загрузки библиотек. Откройте командную строку и введите:

$ pear install OLE
$ pear install Spreadsheet_Excel_Writer

Вот и все. Мы готовы к действию!

Важная заметка! Я использовал PEAR :: OLE версии 0.5 и PEAR :: Spreadsheet_Excel_Writer версии 0.7 для примеров в этой статье. Имейте в виду, что в будущих выпусках все может измениться.

Найти свой путь вокруг

Для начала давайте создадим очень простую таблицу.

 <?php 
// Include PEAR::Spreadsheet_Excel_Writer
require_once "Spreadsheet/Excel/Writer.php";

// Create an instance
$xls =& new Spreadsheet_Excel_Writer();

// Send HTTP headers to tell the browser what's coming
$xls->send("test.xls");

// Add a worksheet to the file, returning an object to add data to
$sheet =& $xls->addWorksheet('Binary Count');

// Write some numbers
for ( $i=0;$i<11;$i++ ) {
 // Use PHP's decbin() function to convert integer to binary
 $sheet->write($i,0,decbin($i));
}

// Finish the spreadsheet, dumping it to the browser
$xls->close();
?>

Имя файла: example_1.php

Укажите в браузере сценарий и, если он знает об Excel (или OpenOffice Calc), откроет электронную таблицу, содержащую числа от 0 до 10 в двоичном виде.

Хранение файлов

В этом случае электронная таблица отображается динамически — на сервере ничего не сохраняется. Если вы хотите создать файл вместо этого, вы можете отключить обработку, необходимую для создания листа, который не изменился, просто передав конструктору правильный путь и имя файла и избегая отправки заголовков HTTP, например, так:

 <?php 
// Has a spreadsheet been created?
if ( !file_exists('sheets/binary.xls') ) {

 // Include PEAR::Spreadsheet_Excel_Writer
 require_once "Spreadsheet/Excel/Writer.php";
 
 // Create an instance, passing the filename to create
 $xls =& new Spreadsheet_Excel_Writer('sheets/binary.xls');
 
 // Add a worksheet to the file, returning an object to add data to
 $sheet =& $xls->addWorksheet('Binary Count');
 
 // Write some numbers
 for ( $i=0;$i<11;$i++ ) {
   // Use PHP's decbin() function to convert integer to binary
   $sheet->write($i,0,decbin($i));
 }
 
 // Finish the spreadsheet, dumping it to the browser
 $xls->close();
}
?>

Ваша электронная таблица готова для скачивания здесь

Имя файла: example_2.php

Если вы используете систему на основе UNIX, не забудьте изменить разрешения для каталога, в котором вы храните электронную таблицу, чтобы PHP мог писать в нее.

Обзор API

ОК, мы закончили с основами. Чтобы получить максимальную отдачу от PEAR :: Spreadsheet_Excel_Writer, вам нужно немного больше узнать об API. Документация по API, доступная на веб-сайте PEAR , сейчас устарела (кажется, она сильно выросла с момента создания этой версии документации). К счастью, авторы, по большей части, добавили встроенную документацию в код, так что вы можете создавать свои собственные документы API, скачав phpDocumentor и указав его в каталог, содержащий все
Spreadsheet_Excel_Writer исходный код. Если вам нужна помощь для начала работы с phpDocumentor (и извините за продвижение), это обсуждается в томе 2: Приложения PHP Anthology .

Основной класс, с которым вы всегда начнете работать, Spreadsheet_Excel_Writer , представляет точку доступа ко всем другим классам в библиотеке. Он предоставляет два важных фабричных метода (которые фактически определены в родительском классе Spreadsheet_Excel_Writer_Workbook :

  • addWorksheet()Spreadsheet_Excel_Writer_Worksheet . Большая часть работы выполняется с экземплярами этого класса (как указано выше), что позволяет записывать в ячейки одного листа (электронная таблица Excel представляет собой рабочую книгу, содержащую одну или несколько рабочих таблиц).
  • addFormat()Spreadsheet_Excel_Writer_Format , который используется для добавления визуального форматирования ячеек на рабочем листе.

Библиотека содержит три других класса, о которых вам следует знать, хотя вам может не потребоваться работать с ними напрямую:

  • Spreadsheet_Excel_Writer_Validator позволяет добавлять правила проверки ячейки. Прямо сейчас, в основном нет документации для этого класса. Кажется, это экспериментальный код, поэтому я буду избегать его здесь. По сути, он предоставляет возможность выполнить базовую проверку данных, введенных в ячейку Excel конечным пользователем. Более сложные правила, такие как проверка по списку ячеек, могут быть реализованы путем расширения класса. Класс Spreadsheet_Excel_Writer_Workbook предоставляет метод addValidator()setValidation()
  • Spreadsheet_Excel_Writer_Parser , который является синтаксическим анализатором для функций электронной таблицы Excel, который позволяет проверить, является ли функция допустимым синтаксисом Excel. Это может помочь вам отлавливать ошибки при добавлении функций в электронную таблицу в PHP.
  • Наконец, Spreadsheet_Excel_Writer_BIFFwriter используется для генерации двоичного формата файлов для хранения файлов Excel. Если вы интересуетесь взломом Excel, может быть интересно изучить, что он делает, но в противном случае библиотека полностью скрывает вас от этого класса, поэтому вам не нужно об этом беспокоиться.

Нулевой индекс путаницы

Одним из методов примечания, который мы видели в приведенном выше примере, является метод Spreadsheet_Excel_Writer_Worksheet::write() Это может немного сбить с толку, если вы привыкли к способу адресации ячеек в Excel.

Первым аргументом write()номер строки . Номер первой строки в верхней части электронной таблицы равен 0 (нулю) в PEAR :: Spreadsheet_Excel_Writer, а не 1, как в Excel.

Второй аргумент — номер столбца . Теперь столбцы в Excel обозначены буквами алфавита, а не цифрами, так что вам просто нужно привыкнуть к переводу между ними. Буква F является 6-ой в алфавите, поэтому второй аргумент равен… 5 (конечно!) — самый левый столбец равен 0 (нулю) в PEAR :: Spreadsheet_Excel_Writer, поэтому вам нужно вычесть один, чтобы получить номер столбца.

Третий аргумент write() есть необязательный четвертый аргумент, используемый для применения визуального форматирования к ячейке.

В классе Spreadsheet_Excel_Writer_Worksheet есть еще много методов, таких как замораживание и оттаивание частей листа и форматирование листа в целом для печати. Я коснусь некоторых из них в следующих примерах, но вам придется изучить большинство для себя.

Добавление форматирования ячеек

Итак, как насчет того, чтобы сделать электронную таблицу красивой? Мы можем сделать это с помощью PEAR :: Spreadsheet_Excel_Writer, используя addFormat() Мы применяем форматирование к этому объекту, используя (большое количество) предоставляемых им методов, а затем передаем ему метод write()

В качестве примера «реального мира», скажем, я хочу дать пользователям моего интернет-магазина phpPetstore.com возможность загрузить «квитанцию» за товары, которые они только что купили, в виде Рабочей книги, содержащей один Рабочий лист.

Я начинаю свой рабочий лист с обычного материала:

 <?php  
require_once "Spreadsheet/Excel/Writer.php";  
 
// Create workbook  
$xls =& new Spreadsheet_Excel_Writer();  
 
// Create worksheet  
$cart =& $xls->addWorksheet('phpPetstore');

Затем (благословленный знанием, что я буду использовать только четыре столбца), я добавлю заголовок на лист, объединяя несколько ячеек, в которые он будет помещен. Здесь вы впервые почувствуете, как выполняется форматирование:

 // Some text to use as a title for the worksheet  
$titleText = 'phpPetstore: Receipt from ' . date('dS M Y');  
 
// Create a format object  
$titleFormat =& $xls->addFormat();  
 
// Set the font family - Helvetica works for OpenOffice calc too...  
$titleFormat->setFontFamily('Helvetica');  
 
// Set the text to bold  
$titleFormat->setBold();  
 
// Set the text size  
$titleFormat->setSize('13');  
 
// Set the text color  
$titleFormat->setColor('navy');  
 
// Set the bottom border width to "thick"  
$titleFormat->setBottom(2);  
 
// Set the color of the bottom border  
$titleFormat->setBottomColor('navy');  
 
// Set the alignment to the special merge value  
$titleFormat->setAlign('merge');  
 
// Add the title to the top left cell of the worksheet,  
// passing it the title string and the format object  
$cart->write(0,0,$titleText,$titleFormat);  
 
// Add three empty cells to merge with  
$cart->write(0,1,'',$titleFormat);  
$cart->write(0,2,'',$titleFormat);  
$cart->write(0,3,'',$titleFormat);  
 
// The row height  
$cart->setRow(0,30);  
 
// Set the column width for the first 4 columns  
$cart->setColumn(0,3,15);

Сначала обратите внимание, что я получил объект форматирования, вызвав addFormat() Затем я применяю определенное форматирование к объекту (имена методов, такие как setBold()

Как только я закончил форматирование, я вызываю write()

Одна необычная вещь, которую я сделал здесь, — это объединить четыре ячейки. Вызвав setAlign (‘merge’) для объекта форматирования (обычно вы используете что-то вроде ‘left’, ‘right’ или ‘center’), я сказал Spreadsheet_Excel_Writer, что он должен объединить все ячейки, к которым применяется это форматирование. их. Вот почему я создал три пустых ячейки и применил к ним форматирование.

Использование setRow() Этот метод имеет дополнительные необязательные аргументы форматирования, которые позволяют, например, применять объект форматирования ко всей строке. Аналогично, для setColumn() Разница в том, что setRow()setColumn()

Все идет нормально. Теперь мне нужно добавить некоторые данные на лист. Чтобы не усложнять пример (с использованием базы данных), я буду использовать индексированный массив ассоциативных массивов, который мы можем притвориться результатом SQL-выбора:

 $items = array (  
 array( 'description'=>'Parrot'  ,'price'=>34.0,  'quantity'=>1),  
 array( 'description'=>'Snake'  ,'price'=>16.5,  'quantity'=>2),  
 array( 'description'=>'Mouse'  ,'price'=>1.25,  'quantity'=>10),  
);

«Столбцы в базе данных» являются ключами массивов второго порядка; «описание», «цена» и «количество», поэтому следующее, что нам нужно сделать, это добавить заголовки столбцов вместе с дополнительным заголовком «Всего», который я буду использовать позже:

 // Set up some formatting  
$colHeadingFormat =& $xls->addFormat();  
$colHeadingFormat->setBold();  
$colHeadingFormat->setFontFamily('Helvetica');  
$colHeadingFormat->setBold();  
$colHeadingFormat->setSize('10');  
$colHeadingFormat->setAlign('center');  
 
// An array with the data for the column headings  
$colNames = array('Item','Price($)','Quantity','Total');  
 
// Add all the column headings with a single call  
// leaving a blank row to look nicer  
$cart->writeRow(2,0,$colNames,$colHeadingFormat);

Вы уже видели форматирование. То, что вы еще не видели, это метод writeRow() По сути, это то же самое, что и write() Это удобно для сокращения строк кода.

Еще одна вещь, которую я хочу сделать, — убедиться, что заголовки столбцов всегда будут видны при прокрутке списка элементов. В Excel это возможно путем «замораживания» «панели» — выбора блока ячеек, который будет «зависать», пока пользователь прокручивает данные, позволяя им видеть заголовки столбцов (в данном случае), которые сообщают им, что данные представляют. То же самое возможно с PEAR :: Spreadsheet_Excel_Writer:

 // The cell group to freeze  
// 1st Argument - vertical split position  
// 2st Argument - horizontal split position (0 = no horizontal split)  
// 3st Argument - topmost visible row below the vertical split  
// 4th Argument - leftmost visible column after the horizontal split  
$freeze = array(3,0,4,0);  
 
// Freeze those cells!  
$cart->freezePanes($freeze);

Обратите внимание, что «замораживание» было применено непосредственно через объект $ cart рабочего листа, а не через объект форматирования, поскольку оно применялось к коллекции ячеек. Форматирование, с другой стороны, применяется к отдельным ячейкам.

Наконец, я перебираю товары в корзине, добавляя данные на лист:

 // Pseudo data  
$items = array (  
 array( 'description'=>'Parrot'  ,'price'=>34.0,  'quantity'=>1),  
 array( 'description'=>'Snake'  ,'price'=>16.5,  'quantity'=>2),  
 array( 'description'=>'Mouse'  ,'price'=>1.25,  'quantity'=>10),  
);  
 
// Use this to keep track of the current row number  
$currentRow = 4;  
 
// Loop through the data, adding it to the sheet  
foreach ( $items as $item ) {  
   // Write each item to the sheet  
 $cart->writeRow($currentRow,0,$item);  
 $currentRow++;  
}

Здесь нет ничего особенно нового, за исключением того, что вы обнаружите, что важно отслеживать номера строк и столбцов при циклическом просмотре данных, поскольку они необходимы для вставки данных в правильное место.

Это в основном это. Если вы новичок в ООП в PHP, это, на первый взгляд, может быть немного пугающим, но вы заметите, что все методы хорошо названы до такой степени, что вы обычно можете угадать их назначение, просто взглянув на них. Понятие извлечения одного объекта из другого может быть новым, но когда вы думаете об этом, имеет смысл создать объект Worksheet, вызвав метод addWorksheetSheet()write()

Добавление функций Excel

Теперь вы можете сделать электронную таблицу, которая выглядит красиво, но, как скажет любой Excel Pro, простое отображение необработанных данных не очень полезно. Жизнь становится действительно интересной, когда вы начинаете использовать функции Excel (и, возможно, ваши собственные) для выполнения вычислений на необработанных данных и превращения их в нечто более интересное.

Сейчас я не мастер Excel (и это не собирается превращаться в учебник Excel), но ясно, что моя квитанция в корзине должна быть умнее, поэтому мне нужно добавить некоторые расчеты на основе данных, которые я уже добавлено. Для каждой строки я хочу отобразить «общую стоимость товара» — необработанные данные содержат цену за единицу товара и количество приобретенных товаров:

 "total item cost" = "unit price" * "number of items purchased"

С точки зрения Excel, для вычисления суммы для элемента в пятой строке формула может быть:

 [Cell D5] =PRODUCT(B5:C5)

Чтобы сделать это с помощью PEAR :: Spreadsheet_Excel_Writer, мне нужно немного изменить код, который перебирает данные:

 // Use this to keep track of the current row number   
$currentRow = 4;  
 
// Loop through the data, adding it to the sheet  
foreach ( $items as $item ) {  
   // Write each item to the sheet  
 $cart->writeRow($currentRow,0,$item);  
   
 // Remember Excel starts counting rows from #1!  
 $excelRow = $currentRow + 1;  
   
 // Create a PHP string containing the formula  
 $formula = '=PRODUCT(B' . $excelRow . ':C' . $excelRow .')';  
   
 // Add the formula to the row  
 $cart->writeFormula($currentRow,3,$formula);  
   
 $currentRow++;  
}

Добавить формулу довольно просто — мы просто используем метод writeFormula() Но наиболее важным (и запутанным) является то, что я упоминал ранее — Excel начинает считать строки с 1, а PEAR :: Spreadsheet_Excel_Writer начинается с 0 (ноль), поэтому при создании функций мне нужно помнить об этом, или я буду ссылаться в неправильные клетки. Вот почему я создал переменную $excelRow$currentRow Вы можете подумать, что это ошибка дизайна со стороны авторов, но помните: в PHP, как и в большинстве языков программирования, индексированные массивы начинаются с нулевого индекса. Попытка превзойти их на единицу, просто чтобы хорошо поиграть с Excel, вероятно, привела бы ко многим ошибкам и головным болям при обслуживании. Если вас это действительно раздражает, подберите несколько функций для перевода между ними.

Итак, теперь мой лист отображает итоги по каждой строке. Но как насчет суммирования итогов, чтобы клиенты могли видеть цифру, которая появится в их счете по кредитной карте? Для этого достаточно просто сложить все итоги по элементам и отобразить результат в другой ячейке.

В терминах Excel мне нужно использовать функцию SUM()

 [Grand Total Cell] =SUM(D5:D7)

Чтобы добавить это в электронную таблицу, после завершения цикла я добавляю следующее:

 // The first row as Excel knows it - $currentRow was 4 at the start   
$startingExcelRow = 5;  
 
// The final row as Excel  
// (which is the same as the currentRow once the loop ends)  
$finalExcelRow = $currentRow;  
 
// Excel formal to sum all the item totals to get the grand total  
$gTFormula = '=SUM(D'.$startingExcelRow.':D'.$finalExcelRow.')';  
 
// Some more formatting for the grand total cells  
$gTFormat =& $xls->addFormat();  
$gTFormat->setFontFamily('Helvetica');  
$gTFormat->setBold();  
$gTFormat->setTop(1); // Top border  
$gTFormat->setBottom(1); // Bottom border  
 
// Add some text plus formatting  
$cart->write($currentRow,2,'Grand Total:',$gTFormat);  
 
// Add the grand total formula along with the format  
$cart->writeFormula($currentRow,3,$gTFormula,$gTFormat);

Опять же, это становится еще более захватывающим, но отслеживание номеров строк в Excel — это, как правило, случай, когда вы не забыли добавить один к той переменной, которая отслеживала номера строк PEAR :: Spreadsheet_Excel_Writer. Также обратите внимание, что я могу применить форматирование к выводу, полученному по формуле.

Наконец, я заканчиваю квитанцию ​​своей корзины покупок, отправляя таблицу прямо в браузер:

 // Send the Spreadsheet to the browser   
$xls->send("phpPetstore.xls");  
$xls->close();  
?>

Имя файла: phpPetstore.php

Вот и все. Электронная таблица готова к загрузке. Готовый код доступен здесь .

Заворачивать

Как вы уже видели, PEAR :: Spreadsheet_Excel_Writer предлагает практически все, что вам нужно для создания полезной электронной таблицы, включая форматирование и функции. И поскольку вы также используете функциональные возможности Excel, в вашем распоряжении много возможностей.

API аккуратный и с ним легко работать. Классы также хорошо структурированы, поэтому сравнительно легко выполнить масштабирование того, что я здесь сделал, до рабочей книги, которая содержит многочисленные взаимосвязанные листы. Однако имейте в виду, что, как вы видели здесь, вы можете получить довольно длинные сценарии, если не будете осторожны, особенно потому, что форматирование должно быть определено в мельчайших деталях. Если вам нужно проделать серьезную работу с PEAR :: Spreadsheet_Excel_Writer, стоит рассмотреть возможности для повторного использования. Вы можете обнаружить, что есть определенный формат ячеек, который постоянно появляется и может быть лучше помещен в класс и использован повторно. Если вы создаете рабочую книгу, содержащую много похожих рабочих листов (например, цифры продаж с разбивкой по рабочим листам для каждого региона), написание классов в качестве шаблона (подсказка шаблона проектирования), который создает листы, может сэкономить много усилий.

В целом, PEAR :: Spreadsheet_Excel_Writer является отличным дополнением к вашему инструментарию PHP, который заставляет ваших пользователей вас беспокоить, потому что они не могут получить «представление» о своих данных, которые вы им предоставляете с помощью HTML, Spreadsheet_Excel_Writer представляет собой удобную альтернативу реализации бесконечный список новых функций. Более того, он создает «вау» фактор, с помощью которого вы можете произвести впечатление на потенциального клиента, особенно если рассматриваемый клиент использует Excel в качестве своего ежедневного хлеба с маслом.

Тема PHP и Excel становится еще более интересной, когда вы рассматриваете Jedox's Worksheet Server , инструмент для чтения (здесь мы только что писали) электронных таблиц Excel и создания из них PHP-приложений. Но это история в другой раз ...