Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM    Subskrybuj kanał ATOM dla tagu zend_framework Kanał ATOM (tag: zend_framework)

Autor wpisu: Diabl0, dodany: 29.01.2009 12:10, tagi: zend_framework, php

DataGrid for Zend Framework

DataGrid for Zend Framework

Wczoraj wieczorem na forum zend-framework.pl pojawił się link do ciekawego projektu realizującego tak oczekiwany przez wielu scaffolding.  Projekt nazywa się DataGrid for Zend Framework i obudził moje nadzieje na szybkie implementacje CRUD w rozwijanych przeze  mnie projektach. Niestety, nie jest jeszcze aż tak różowo :(

Po wstępnych testach mogę już napisać parę słów co o tym sądzę, i niestety optymizm miesza się z garścią zawodów. O ile sam “grid” działa bezproblemowo, można szybko i prosto wstawiać sortowalne i paginowalne (dziwne słowo) tabele do projektów, to sposób realizacji jest już mniej przyjemny.

Niestety, cały system działa w oderwaniu od zendowskich modeli. Inicjalizacja grida wygląda mniej więcej tak:

$grid = new $grid (  $db, 'Grid Example', APPLICATION_PATH . 'public_html/temp', array ('download', 'save' ) );
$grid->from ( 'table' )->order ( 'id ASC' );

Jak widać wewnątrz kontrolera musimy nie tylko mieć adapter bazy, ale także znać dokładną nazwę tabeli.  W moim przypadku kiedy samych baz w projekcie mam 6, a tabel nawet nie liczyłem, jest to kłopotliwe.

Kolejny problem wynikający z niekorzystania z modeli, jest niemożliwość zachowania logiki biznesowej która w takich modelach może się znajdować.  Bardzo często w modelach wykorzystuję $_rowClass i możliwości oferowane przez takie metody jak Zend_Db_Table_Row::_insert() Zend_Db_Table_Row::_postUpdate().  Pozwala mi to automatycznie filtrować dane, czyścić odpowiedni cache przy modyfikacjiach, tworzyć/modyfikować powiązane wpisy itp. Oderwanie grida od modelu uniemożliwia natomiast takie rozwiązania i zmusza do dodatkowego kodu.

Resumując, na obecnym etapie ten komponent może się sprawdzić jako narzędzie do szybkiego przeglądu zawartości tabel (zwłaszcza jeśli w modelu zaimplementujemy sobie metodę w rodzaju getGrid), ale jeśli chodzi o funkcjonalność CRUD to oderwanie ich od modeli w moich oczach dyskfalifikuje to rozwiązanie. Szkoda.

Related posts

Autor wpisu: Diabl0, dodany: 16.01.2009 10:28, tagi: php, zend_framework

Zend_Captcha_Image

Zend_Captcha_Image

Wczoraj przy okazji drobnych modyfikacji w jednym z starszych projektów zaszła potrzeba użycia mechanizmu Captcha. Oczywiście pierwsze spojrzenie padło na komponent Zend_Captcha. Po drobnych walkach związanych z błędami w dokumentacji (konkretnie przykład użycia isValid()), udało nam się doprowadzić ją do działania, i jak na razie się sprawdza. Przy okazji jednak zauwżyłem brak jednej, IHO bardzo przydatnej funkcjonalności jaką jest Factory. A skoro czegoś nie ma, to trzeba to sobie napisać ;)

Zanim przejdę do klasy Factory, dla potomności i innych drobna uwaga na temat użycia isValid(). Dokumentacja ZF zawiera następujący przykład:


if ($captcha->isValid($_POST['foo'], $_POST)) {
    // Validated!
}

Natomiast z praktyki i analizy kodu wyszło nam że prawidłowa kontrukcja to:

			if ($captcha->isValid(
				array(
					'input' => $this->getRequest()->getParam('foo'),
					'id' => $this->getRequest()->getParam('id')
				) )) {
			    // Validated!
			}

To tyle dla potomności, teraz wracamy do sprawy Factory.

Dlaczego klasa faktorująca jest tak istotna? Przyjrzyjcie się jak działa Zend_Cache czy Zend_Db. W onfigu trzymamy sobie parametry konfiguracyjne, a w samym kodzie wywołujemy przykładowo:

$cache = Zend_Cache::factory ( $config->cache->frontend, $config->cache->backend, $config->cache->frontendOptions->toArray(), $config->cache->backendOptions->toArray());

Chcąc zmienić metodę cachowania czy dowolny parametr, robimy to w jednym pliku bez potrzeby wgłębiania się w kod i wyszukiwania wszystkich wystąpień. Natomiast w przypadku Zend_Captcha, każdorazowo w kodzie musimy decydować jakiego adaptera używamy oraz przekazywać parametry. Używając Zend_Captcha w kilku miejscach, i potrzebując nagle zmienić adapter np. z Zend_Captcha_Figlet na Zend_Captcha_Image, musimy wyszukać wszystkie wystąpienia, i ręcznie je zmienić.

Dlatego napisałem sobie Mao_Captcha::factory() :)

Przykłady użycia:

Standardowy Zend_Captcha_Image

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: Diabl0, dodany: 07.01.2009 23:43, tagi: php, zend_framework

Zapewne wielu z nas często potrzebuje umieścić 2 elementy obok siebie używając Zend_Form. Dotychczas wydawało mi się że jedynym sposobem na to jest tworzenie własnych widoków formy. Jednak MAZI na forum Zend Framework polska społeczność znalazł sposób aby uzyskać ten sam efekt za pomocą dekoratorów bez potrzeby używania własnych widoków formy.

	    $submit->setDecorators(
	    	array(
	    		'ViewHelper',
	        	array(
	        		array(
	        			'data' => 'HtmlTag'
	        		),
	        		array(
	        			'tag' => 'td',
	        			'class' => 'element',
	        			'placement' => 'prepend',
	        			'openOnly' => 'true'
	        		)
	        	),
	        	array(
	        		array(
	        			'label' => 'HtmlTag'
	        		),
	        		array(
	        			'tag' => 'td',
	        			'placement' => 'prepend'
	        		)
	        	),
	        )
	    ); 

	    $cancel->setDecorators(
	    	array(
	        	'ViewHelper',
	        	array(
	        		array(
	        			'data' => 'HtmlTag'
	        		),
	        		array(
	        			'tag' => 'td',
	        			'class' => 'element',
	        			'placement' => 'append',
	        			'closeOnly' => 'true'
	        		)
	        	),
	        )
		);

Related posts

Autor wpisu: Diabl0, dodany: 23.12.2008 17:15, tagi: zend_framework

KalendarzBardzo często tworząc różnego rodzaju formularze korzystamy z pól do wprowadzania daty. Miłym uzupełnieniem dla użytkowników takiego pola jest dodanie kalendarza z którego mogą sobie oni wybrać datę. Jednym z najbardziej znanych narzędzi tego typu jest The Coolest DHTML Calendar. A jako zwolennik Zend_Form, zaraz pokażę wam jak ułatwić sobie życie i stworzyć Zend_Form_Element do szybkiego i prostego podłączania takiego kalendarza do formy.

Dotychczas chcąc dać użytkownikom możliwość wyboru daty z kalendarza używałem albo Dojo i jego dijit.form.DateTextBox (rozwiązanie mocno ograniczone pod względem możliwości), albo tworzyłem w formie zwykły element text i w widoku formy dodawałem kod odpowiedzialny z a DHTML Calendar. Aż ostatecznie dzisiaj rano stwierdziłem że można to rozwiązać znacznie prościej i wygodniej pisząc własny element Zend_Form_Element. I poniżej znajdziecie tego wstępne rezultaty.

Zacznijmy od elementu formy:

/**
 * calendar form element
 *
 * @uses Mao_View_Helper_FormCalendar
 */
class Mao_Form_Element_Calendar extends Zend_Form_Element_Xhtml
{
    /**
     * Default form view helper to use for rendering
     * @var string
     */
    public $helper = 'formCalendar';

}

Klasa ta jest kalką Zend_Form_Element_Text i jej głównym przeznaczeniem jest określenie helpera widoku - formCalendar gdzie odbywa się większość “magii

/**
 * Helper to generate a "calendar" element
 *
 * @todo uporządkować
 * @todo obsługa parametrów
 */
class Mao_View_Helper_FormCalendar extends Zend_View_Helper_FormElement
{
    /**
     * Generates a 'text' element.
     *
     * @access public
     *
     * @param string|array $name If a string, the element name.  If an
     * array, all other parameters are ignored, and the array elements
     * are used in place of added parameters.
     *
     * @param mixed $value The element value.
     *
     * @param array $attribs Attributes for the element tag.
     *
     * @return string The element XHTML.
     */
    public function formCalendar($name, $value = null, $attribs = null)
    {
        $info = $this->_getInfo($name, $value, $attribs);
        extract($info); // name, value, attribs, options, listsep, disable

        // build the element
        $disabled = '';
        if ($disable) {
            // disabled
            $disabled = ' disabled="disabled"';
        }

        // XHTML or HTML end tag?
        $endTag = ' />';
        if (($this->view instanceof Zend_View_Abstract) && !$this->view->doctype()->isXhtml()) {
            $endTag= '>';
        }

        $xhtml = '<input type="text"'
                . ' name="' . $this->view->escape($name) . '"'
                . ' id="' . $this->view->escape($id) . '"'
                . ' value="' . $this->view->escape($value) . '"'
                . $disabled
                . $this->_htmlAttribs($attribs)
                . $endTag;

        if ( !$disable) {

            $this->view->headLink ()->appendStylesheet('http://cdn.local.ss/js/calendar/calendar-system.css');
            $this->view->headScript()
                ->appendFile ( 'http://cdn.local.ss/js/calendar/calendar.js', 'text/javascript')
                ->appendFile ( 'http://cdn.local.ss/js/calendar/lang/calendar-en.js', 'text/javascript')
                ->appendFile ( 'http://cdn.gazeta.ie/js/calendar/calendar-setup.js', 'text/javascript');

            $xhtml .= '<button' . ' id="' . $this->view->escape($id) . '_trigger"' . '><img src="http://cdn.local.ss/images/crystal/16x16/actions/1day.png"></button>';

            $xhtml .= '
            <script type="text/javascript">
                //<![CDATA[
    			  Calendar.setup(
    			    {
    			      inputField  : "' . $this->view->escape($id) . '",         // ID of the input field
    			      ifFormat    : "%Y-%m-%d",    // the date format
    			      button      : "' . $this->view->escape($id) . '_trigger"       // ID of the button
    			    }
    			  );
    		    //]]>
        	</script>
    		';
        }

        return $xhtml;
    }
}

Początek jest żywcem przekopiowany z dowolnego Zend_View_Helper_Form*, jednak końcówka zawiera już sam miodzik

  1. po sprawdzeniu czy element nie jest wyłączony ( $disabled ) za pomocą helperów headLink i headScript dodajemy linki do skryptów JS i CSS.  W powyższym przykładzie odpowiednie pliki znajdują się na firmowym CDNie i są wpisane na stałe. W ostatecznym kodzie wypadało by użyć helpera baseUrl.
  2. do pola <input type=”text” (zawartego w zmiennej $xhtml) dodajemy element button który będzie naszym triggerem (id = $id . ‘_trigger’)
  3. Dodawany jest kawałek kodu JS inicializujący DHTML dla triggera

I… to na razie wszystko, gdyż moja doza zapału w te święta chwilowo się wyczerpała ;) Oczywiście, cały zaprezentowany powyżej kod to tylko szybka wstępna wersja (szkic) który należy jeszcze odpowiednio rozbudować (np. możliwość ustalania formatu daty z poziomu formy, własny kod triggera itp), ale po świętach kogoś do niego posadzę i może kiedyś zaprezentuję bardziej rozbudowaną i elastyczną wersję. W ostateczności powinna to być także dobra podstawa do własnych eksperymentów jako prosty i działający przykład własnego elementu.

Related posts

Autor wpisu: Athlan, dodany: 20.12.2008 15:00, tagi: zend_framework

Pisząc nowy projekt natknąłem na problem z procesem uploadu filmiku do serwisu YouTube. Sam upload jest bardzo łatwy do napisania z Zend_Gdata_YouTube - przykład można znaleźć w manualu. Myślę, że zainteresowani przeczytają manual i wszystko będzie jasne. Więc jeżeli to takie proste, to w czym problem?

Zakładamy, że userzy uloadują filmiki bezpośrednio na nasz serwis. Nasz serwer ma za zadanie:

  1. Skompresować video i zapisać go w formacie flv.
  2. Nałożyć watermark.
  3. Wysłać obrobiony film do serwisu Youtube logując się na zdefiniowane przez użytkownika konto lub założone przez administratora strony.

Pierwsze 2 kroki wykonają się błyskawicznie w porównaniu do trzeciego. Kompresja i nałożenie watermarku na 30 megowy plik z wykorzystaniem FFMPEG to nic nadzwyczajnego. Natomiast wysyłka pliku na serwery Youtube’a może zawiesić apache’a, gdy jest ich kilka.

Rozwiązanie? Wpadłem na pomysł, aby upload filmików ustawiony był w pewnego rodzaju kolejce, która uruchamiana by była co minutę (cron), a czas jednego wysłania elemntu nie mógłby przekroczyć 50 sekund. Oczywiście takie działanie uruchamiałoby swój osobny proces apache’a. Ten sposób jest ograniczony dwoma limitami: wielkością pliku oraz czasem jego uploadu na serwer (jednocześniem interwałem uruchamiania kolejki).

Autor wpisu: Diabl0, dodany: 19.12.2008 21:53, tagi: zend_framework, php

Tym razem zadanie wydawało się banalne - ułatwić życie użytkownikom umożliwiając im masowe drukowanie zleceń raz na jakiś czas, zamiast przy każdym nowym zleceniu. Zlecenia w formacie PDF były zapisywane do plików. Teraz wystarczyło tylko połączyć te kilka/naście plików w jeden wielostronnicowy PDF (użytkownicy powiesili by mnie za jaja jakby musieli klikać po koleji każdy PDF i klikać “Drukuj”). Tylko…

Pierwszy pomysł jaki mi się nasunął to Zend_Pdf. Widziałem tam i Zend_Pdf_Page, i możliwość wczytania gotowego pliku. Prawie się zdziwiłem na myśl że w końcu ten  IMHO bezcelowy komponent Zend_Pdf do czegoś się przyda. Niestety, po raz kolejny okazało się że jest nieprzydatna do niczego. Pomijając problemy z wczytaniem PDF’a wygenerowanego przez TCPDF:

(”Unsupported PDF version. Zend_Pdf supports PDF 1.0-1.4. Current version - ‘1.7′“)

gdzie w kodzie warunek wygląda następująco:

        $pdfVersion = (float)substr($pdfVersionComment, 5);
        if ($pdfVersion < 0.9 || $pdfVersion >= 1.61) {
            /**
             * @todo
             * To support PDF versions 1.5 (Acrobat 6) and PDF version 1.7 (Acrobat 7)
             * Stream compression filter must be implemented (for compressed object streams).
             * Cross reference streams must be implemented
             */
            throw new Zend_Pdf_Exception(sprintf('Unsupported PDF version. Zend_Pdf supports PDF 1.0-1.4. Current version - \'%f\'', $pdfVersion));
        }

To jeszcze przy próbie utworzenia nowego dokumentu i przypisania mu stron z innego dokumentu ładny wyjątek:

Page is attached to one documen, but rendered in context of another.

Pozostało w takim razie Google, a tam… niewiele możliwości. Albo komercja, albo rozwiązania systemowe, i… mała perełka: FPDI

FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF, which was developed by Olivier Plathey. Apart from a copy of FPDF, FPDI does not require any special PHP extensions.

Since version 1.2.1, FPDI only supports the 1.6 version of FPDF.

Po chwili moją uwagę przyciągnęło jeszcze jedno (dość ciekawie brzmiące) zdanie:

Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Autor wpisu: Diabl0, dodany: 26.11.2008 12:15, tagi: zend_framework, php

Dzisiaj ponad 2 godziny spędziłem na dochodzeniu dlaczego Zend_Auth zapisuje do sesji “okrojony” rekord usera (bazujący na Zend_Db_Table_Row).

Rekord ten zawierał garść dodatkowych informacji (jak na przykład lista grup do których należy user) wypełnianych przed przekazaniem go do Zend_Auth_Storage, które jednak nie zapisywały się tam. Dopiero dokładna analiza wykazała że winowajcą była metoda __sleep() w Zend_Db_Table_Row_Abstract

Lekarstwem okazało się nadpisanie w modelu usera ( Main_Model_Users_Row ) tej metody, i tak teraz mój model wygląda mniej więcej tak:

class Main_Models_Users_Row extends Mao_Db_Table_Row {

	/**
	 * tablica z row'ami grup usera
	 *
	 * @var array
	 */
	private $_groups;

	/**
	 * trzeba przeciazyc sleepa i do tablicy $ret
	 * dodac wszystkie pola, ktore maja byc zserializowane
	 *
	 * @return array
	 */
	public function __sleep() {
		$ret = parent::__sleep ();
		$ret [] = '_groups';

		return $ret;
	}

}

Related posts

Wszystkie wpisy należą do ich twórców. PHP.pl nie ponosi odpowiedzialności za treść wpisów.