PHPExcel: tabulky jednoduše

Na internetu je nepřeberné množství řešení pro generování dokumentů ve formátu Excel. Leckdy zákazník právě takový export dat požaduje. V tomto článku si ukážeme, jak pracovat s jednou z nejrozsáhlejších knihoven, totiž s knihovnou PHPExcel. Funkčností a možnostmi se blíží téměř samotnému Excelu.
Nálepky:
Knihovna PHPExcel vznikla koncem roku 2006 s cílem poskytnout plný OOP přístup pro generování OpenXML dokumentů (tedy souborů s příponou .docx, .xlsx atp.), nicméně dnes umí generovat i jiné formáty: klasický XLS, PDF a dokonce i HTML tabulku. Případů, kdy potřebujeme z aplikace exportovat dokument ve formátu Excel, je nepřeberně. V článku si jako příklad vygenerujeme náš MP3 archiv.
Struktura tabulky
Každá tabulka se skládá ze souboru (tedy objekt PHPExcel) a jednotlivých listů (PHPExcel_Worksheet). Vždy tedy musíme začít vytvořením dokumentu:
$dokument = new PHPExcel();
Logický předpoklad je samozřejmě začít generovat data, nicméně objektový návrh předpokládá, že listů budeme mít více. Proto musíme vždy vybrat aktuální list a poté nám již nic nebrání v samotném plnění příslušných buněk.
<?php $dokument->setActiveSheetIndex(0); // Zde si vyvoláme aktivní list (nastavený nahoře) a vyplníme buňky A1 a A2 $list = $dokument->getActiveSheet(); $list->setCellValue('A1', 'Ahoj'); $list->setCellValue('A2', 'Světe'); ?>
Zápis souboru je realizován za pomoci tzv. writerů – sami si tedy vybereme, který chceme použít a soubor uložíme. Zde náš první soubor uložíme v novém XLSX a následně ve starém XLS:
<?php // Nyní si zavoláme writer pro XLSX a soubor uložíme: $newExcelWriter = new PHPExcel_Writer_Excel2007($dokument); $newExcelWriter->save('./ukazka.xlsx'); // A analogicky uděláme to samé pro starý formát: $oldExcelWriter = new PHPExcel_Writer_Excel5($dokument); $oldExcelWriter->save('./ukazka.xls'); ?>
Chcete se naučit o PHP víc?
Akademie Root.cz pořádá školení Kurz programování v PHP5. Jednodenní kurz programování v PHP 5 je určen všem webovým vývojářům, kteří se chtějí do hloubky seznámit a sžít s programovacím jazykem PHP ve verzi 5. První část kurzu je zaměřena na nový objektový model se všemi jeho vlastnostmi, ošetření chyb pomocí výjimek a efektivní využití těchto konceptů. Druhá část je zaměřena na nové knihovny PHP 5, především pro práci s databázemi, XML a objekty. Pozornost je věnována i zajištění kompatibility s PHP 4, přechodu z této verze a výhledu na PHP 6. Máte zájem o jiné školení? Napište nám!
Knihovnu lze analogicky použít i pro prosté načítání dokumentů (i když ze zkušenosti pro tento účel doporučuji Excel_Reader). Pro načtení dat musíme nejdříve získat buňku samotnou, ze které následně přečteme její aktuální hodnotu:
echo $list->getCell('A2')->getValue();
Zatím vše vypadá stejně jako ostatní dostupné knihovny, nicméně PHPExcel umí jednu věc, kterou jiné knihovny většinou neumí – stylování jednotlivých buněk. Můžeme tedy definovat barvu pozadí a textu, písmo a jeho velikost, zarovnání buňky i styl okrajů, a to bez větších problémů. Je rovněž možné přidat i obrázky, klikatelné odkazy a spoustu dalšího. Zde však nemáme dostatečný prostor na pokrytí celé knihovny – více je v přiložených ukázkách, místo toho si ukážeme, jak pracovat s okraji:
<?php $list->getStyle('A1') ->getBorders() ->getLeft() ->setBorderStyle(PHPExcel_Style_Border::BORDER_HAIR); // A pravý bude tečkovaný $list->getStyle('A1') ->getBorders() ->getRight() ->setBorderStyle(PHPExcel_Style_Border::BORDER_DOTTED); ?>
Generujeme v praxi
Může se zdát, že zvolený přístup je příliš složitý a osobně s vámi bezvýhradně souhlasím. Uvědomují si to i vývojáři, proto nám připravili dva užitečné způsoby jak styly definovat – nicméně i přesto musíme vybrat příslušnou buňku. Na druhou stranu, selektor pro buňku může být stejný jako pro funkce v excelu:
// Všechny buňky mezi A1 a F56 $sheet->getStyle('A1:F56')-> ... ->setBorderStyle(...);
Kromě toho můžeme styly definovat z pole:
$sheet->getStyle('A5')->applyFromArray($pole);
Analogicky je možné z pole stejně dobře definovat např. pouze okraje:
$sheet->getStyle('A5')->getBorders()->applyFromArray($pole);
Pro zevrubný popis možností doporučuji soubor 05featuredemo.inc.php, který naleznete ve složce Tests v rámci samotné distribuce.V praxi se mi nejvíce osvědčilo zkombinovat funkci čtení a zápisu – pro příklad generování si proto nejdříve vytvoříme prázdný soubor pouze s hlavičkou, samotná data doplníme následně skriptem. Takto si můžeme šablonu pohodlně připravit a v samotném PHP se starat jen o styly, které potřebujeme – například okraje.
V následujícím příkladu máme zdrojový soubor, který obsahuje zdrojové pole (nakonec jde přece jenom o ukázku) a my se soustředíme pouze na to podstatné: samotné načtení a vygenerování databáze MP3. Navíc si ukážeme, jak se nastavují vlastnosti dokumentu.
<?php // Nejdříve si tedy nastavíme styl, který bude aplikován na plné řádky $stylOkraj = array( 'allborders' => array( 'style' => PHPExcel_Style_Border::BORDER_THIN, 'color' => array( 'rgb' => '000000', ), ), ); // Teď načteme šablonu a získáme první list require_once './PHPExcel.php'; require_once './PHPExcel/IOFactory.php'; $dokument = PHPExcel_IOFactory::load('./archiv.sablona.xls'); $dokument->setActiveSheetIndex(0); $list = $dokument->getActiveSheet(); // Nyní začneme zapisovat / začínáme od třetího řádku, protože // v prvních dvou již máme hlavičku. $aktualniRadek = 3; foreach($archiv as $autor => $alba) { // Nastavíme autora do sloupce A (index 0) a aktuálního řádku. Rovnou přidáme i styl s okrajem $list->setCellValueByColumnAndRow(0, $aktualniRadek, $autor); $list->getStyleByColumnAndRow(0, $aktualniRadek)->getBorders()->applyFromArray($stylOkraj); // A zapíšeme všechna alba. Po každém zapsání musíme přičíst aktualní řádek // Společně s tím nastavíme okraj buňce s albem. foreach($alba as $album => $pisnicky) { $list->setCellValueByColumnAndRow(1, $aktualniRadek, $album); $list->getStyleByColumnAndRow(1, $aktualniRadek)->getBorders()->applyFromArray($stylOkraj); $aktualniRadek++; } // Nakonec přidáme jednu mezeru $aktualniRadek++; } // Nastavíme vlastnosti dokumentu: // - aktuální datum kdy byl dokument vytvořen (PHPExcel pracuje s tzv. unix timestamp) // - jméno autora dokumentu $vlastnosti = $dokument->getProperties(); $vlastnosti->setCreated(time()); $vlastnosti->setCreator('Pavel Ptáček pro zdrojak.root.cz'); // A soubor uložíme $writer = PHPExcel_IOFactory::createWriter($dokument, 'Excel5'); $writer->save('./archiv.xls'); ?>
Zde si můžete stáhnout uvedený příklad s rozšířenými možnostmi.
Co jsme si neukázali, je například Serialized writer, který umožní celý dokument uložit do souborů, jež můžete načíst kdykoli jindy. Pokud vše zkombinujeme s ostatními způsoby výstupu, můžeme jednoduše vytvořit alternativu ke Google Documents – není problém stejnou tabulku nabídnout v PDF, HTML tabulce (na webu), obou formátech XLS a nakonec vše ukládat v databázi (a vše generovat přes noc).
PHPExcel je tedy knihovnou, která poskytuje příjemný přístup ke generování, ale i načítání prakticky jakýchkoliv tabulek.
Jen taková zajímavost – mezi přispěvateli do kódu je uveden i Jakub Vrána http://phpexcel.codeplex.com/wikipage?…
To je pravda, napsal jsem načítání a integroval čtení a zápis starších verzí Excelu. Viz také můj starší článek http://php.vrana.cz/…ml-v-php.php
Zajímavosti jsem si nevšiml, nicméně na článek jsem narazil již před nějakou dobou – zrovna když jsem knihovnu s funkcemi PHPExcel hledal :)
Knihovna je skutečně skvělá a není problém vygenerovat téměř cokoliv. Na co však chci upozornit případné zájemce, zápis do XLSX (ostatní jsem nezkoušel) je hodně drahý v případě, že stylujete buňky a provádíte další šílenosti. Další věc, kterou knihovna žere po megabajtech, je operační paměť. Načtení existujícího nebo vytvoření vlastního souboru je opravdu náročné.
Na levném hostingu se vám toto může vymstít.
Myslete na to a řádně testujte.
Je tu velký prostor pro vylepšování knihovny, snad se k tomu někdy někdo dostane.
pouzival som ju v jednom projekte kde som ale potreboval generovat pomerne velke dokumenty (niekolko desiatok zaloziek a kazda obsahovala pomerne vela buniek) a v takom pripade bola tato kniznica skoro nepouzitelna.Bezne phpcko pri generovani zozralo aj cez 256MB ram a viac a tiez doba generovania bola obcas dost dlha. Na male veci je to fajn ale na generovanie vecsich sheetov to nieje moc vhodna kniznica.
Co se paměťové náročnosti týče, v současné době patřím k těm, kteří to pro dané projekty nemusí řešit.
Nicméně, napadá mne několik řešení:
1.) Sdílené styly (aspoň tak jsem to pochopil) viz http://phpexcel.codeplex.com/…ad/View.aspx?… . Ani duplicateStyle ani setSharedStyle se mi nepodařilo rozchodit, ale pokud to vnímám správně pak by shared style daný styl měl zapsat do souboru jenom jednou. Pokud nastavujete styl pro buňky A1:Z2540, pak bych si tipl, že PHPExcel vytvoří v paměti jeden styl pro jednu buňku – v tomto případě se pak bavíme o 32*2540 což je samozřejmě nechutné pro paměť.
Shared styles – pokud se vám je podaří rozchodit – by tento problém měli efektivně odstranit.
2.) Generovat vždy jeden worksheet do samostatného souboru – v podstatě rozdělit práci. V praxi bych to řešil vyexportováním 5 souborů (každý s jedním worksheetem) a pak měl skript, který vše sloučí dohromady – .xlsx soubor je jenom prostý ZIP, tím pádem by mělo stačit vše nakopírovat do složky „worksheets/“ a příslušné upravit soubor workbook.xml (viz článek pana Vrány: http://php.vrana.cz/…ml-v-php.php)
Podobným způsobem v současnosti řeším mass import souborů do CMS kde mám tři skripty:
1.) ten, co rozbalí ZIP od uživatele do dočasné složky, nakonec pošle header(„Location: „) na importovací skript
2.) ten, co prochází dočasnou složku a po každých 500 souborech pošle header(“Location: „) sám na sebe. Pokud již nemá co importovat, zašle header zpět na hlavní skript
3.) Skript který volám z konzole – tzv. entry point. Ten je zadán do crontabu a používá curl na načtení mass importu.
Pokud zkombinujeme oba přístupy, pak by jsme mohli docílit kýženého výsledku. Napadá mne rovněž vše vygenerovat v PHPExcel do jednoho worksheetu vždy s jedním stylem – a styly pak rozkopírovat skriptem jiným, který bude editovat přímo obsah jednotlivých xml souborů v rámci .xlsx souboru.
Jedná se o teorii, nicméně je to pořád příjemnější než jedna knihovna na kterou jsem narazil také – ta generovala klasickou HTML tabulku a vše ukládala do .xls souboru . Zábavné je, že to fungovalo (nicméně, bez diakritiky…)
Pokud tu náročnost na paměť budete dál řešit a naleznete nějaké chytré řešení, budu rád pokud dáte vědět.
Kod tohoto som nevidel, ale neverim na efektivnost objektoveho PHP kodu a mam pocit, ze programator sa zaobera skor cistotou kodu z hladiska POHLADU druheho programatora co to bude citat, ako efektivnostou, co sa nakoniec niekde vypomsti.
vyborny clanok
O této knihovně jsem se dozvěděl (a začal ji používat) cca před 2 roky. Nadšeně jsem začal generovat přímo sestavy v XLS, po čase jsem toho nechal – uživatelé portálu si začali zvyšovat nároky a na to tato knihovna opravdu není dělaná. Jak už zde bylo uvedeno, jakmile na list vložíte více buněk, nějaké to formátování (ať to opravdu vypadá jako tabulka) či výpočtů (ať využijete význam souboru XLS) tak se dostanete do takových časových a paměťových nároků, že vás přejde chuť. Před časem jsem si stáhnul aktuální verzi, abych se přesvědčil zda už je na tom lépe
Generování do XLS jsem tak vzdal, tedy až do doby až naleznu vhodnou alternativu. PHPExcel je dobrý počin, ale plusy (featury a možnosti formátu XLS) bohužel narážejí na aktivní a efektivní využití. Pokud potřebujete jednoduché tabulky vystačíte si s formáty HTML či PDF. Pokud potřebujete složitější tabulky, myslím že PHPExcel není zrovna košér řešení :-(
Budu vděčen za jakýkoliv odkaz směřující na alternativu k PHPExcel.
http://www.kosek.cz/…group08.html
vyzkoušel sem mnoho méně náročných knihoven, ale ani jedna neuměla vygenrovat XLS s češtinou a tím sem skončil
Ja používam php_writeexcel a so slovenčinou som problém nemal. Takže asi ani so slovenčinou nebude. Stačí skonvertovať text na cp1250.
Takže ani s češtinou asi nebude – malo byt :)
Taky jsem to řešil, nakonec jsem našel způsob jak opatchovat knihovnu z php pear, ale ta zase neumí xlsx.
http://wiki.slfree.net/…do_XLS_v_PHP
Možná toto někomu pomůže. Málokdo ví, že Microsoft Office podporuje jako nativní formát také XML/HTML, a to již od verze 2000. Kupříkladu Excel umí otevřít HTML soubor, a data z první tabulky, kterou najde v tomto souboru, převede do buněk a patřičně oformátuje.
Při troše hraní s Content-Type výstupní hlavičkou, a generováním HTML souboru lze vygenerovat relativně jednoduše z PHP jednolistový „excelovský“ soubor.
PS: Nedávno Microsoft zveřejnil kompletní Office HTML/XML referenční manuál. ( ke stažení na http://msdn.microsoft.com/…e.10%29.aspx ) V tomto manuálu jsou popsané veškeré jemnosti Microsoftího HTML, včetně CSS atributů začínajících na -mso-, a jejich vlastností. Lze se zde dozvědět, jak formátovat odrážky pro Word, zalamování textu pro Excel, atp. A to vše lze použít pro vygenerování HTML souboru, který je poté předhozen Officím programům, a správně načten.
http://dtbaker.com.au/random-bits/php-easily-generate-an-excel-file.html
Samozřejmě OOP je hezké a PHPExcel jistě umožňuje mnohem víc věcí než „mé“ řešení, ale proč to nezkusit..
Jen se nestíhám divit, jak nečitelné Captchy se dají v diskusích vymyslet.
Dodávám, že pro správnou češtinu je nutno po odeslání headerů dopsat klasicky cca toto:
<!DOCTYPE html PUBLIC „-//W3C//DTD XHTML 1.0 Transitional//EN“ „http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>
<html xmlns=“http://www.w3.org/1999/xhtml“ xml:lang=“en“ lang=“en“>
<head>
<meta http-equiv=“Content-Type“ content=“text/html; charset=utf-8″ />
<meta name=“language“ content=“cs“ />
<title>titulek</title>
</head>
<body>