Symfony 1.2 i ExtJS - wyświetlanie danych w gridzie

Ostatnio miałem przyjemność poznać i przetestować możliwości frameworka JavaScript: ExtJS. Jest to biblioteka przeznaczona do budowania zaawansowanych interfejsów użytkownika, w szczególności paneli administracyjnych.

W niniejszym wpisie chciałbym na prostym przykładzie zademonstrować możliwości jednego z komponentów tego potężnego frameworka - GridPanel . Jeszcze nie wiem czy będzie to część większego cyklu kursów o extjs i czy pojawi się on także w wersji angielskiej (wersja angielska - dodatkowo zaktualizowana dla Symfony 1.4 i Doctrine), ale narazie w planie mam wpisy dotyczące integracji z zaawansowanym systemem filtrów (ExtJS filter plugin) i dodawania/edytowania elementów do grida za pomocą modalnych okien - o ile oczywiście tematyka tego wpisu spotka się z zainteresowaniem.

Konfiguracja

Niezbędna konfiguracja dla tutoriala:

  • dowolny serwer z obsługa PHP w wersji co najmniej 5.2.4 + baza danych MySql;
  • zainstalowany framework Symfony w wersji 1.2.X (nic nie stoi na przeszkodzie by wypróbować sf 1.3) lub ewentualnie sandbox do pobrania (piaskownica Symfony- wstępnie skonfigurowany framework gotowy do pracy od zaraz);
  • dostęp do wiersza poleceń;

Rozpoczynamy prace
Mając tak przygotowane środowisko należy utworzyć nowy projekt, wygenerować aplikacje frontendu (w przypadku korzystania z piaskownicy jest już wygenerowana) i przykładowy moduł, np: grid. By tego dokonać w katalogu projektu z wiersza poleceń należy wykonać kolejno:


symfony generate:project sfExtTest

symfony generate:app frontend

symfony generate:module grid

Połączenie z bazą danych i dane testowe
Dla naszego prostego przykładu moglibyśmy wykorzystać statyczne dane utworzone w JavaScript, jednakże chce pokazać przepływ danych między aplikacją klienta, a serwerem i bazą danych (między framewrokiem ExtJS, a Symfony i bazą danych), także niezbędna będzie dodatkowa konfiguracja.

Należy skonfigurować połączenie z bazą danych, utworzyć testowe tabele i wczytać do nich dane testowe. Poniższy kod odpowiada za wymienione powyżej elementy (poniższe dane zostały już wcześnie wykorzystane w tutorialu).

Konfiguracja połączenia z bazą danych:


symfony configure:database “mysql:host=localhost;dbname=twojaBazaDanych” uzytkownik haslo

Struktura tabeli testowej:


propel:
  country:
    _attributes: { phpName: Country }
    id:
    name: varchar(50)
    description: varchar(255)
    created_at:

Dane testowe:


Country:
  Country_1:
    name: 'Poland'
    description: 'Description for Poland'
  Country_2:
    name: 'Germany'
    description: 'Description for Germany'
  Country_3:
    name: 'Brazil'
    description: 'Description for Brazil'
  Country_4:
    name: 'USA'
    description: 'Description for USA'
  Country_5:
    name: 'Ireland'
    description: 'Description for Ireland'
  Country_6:
    name: 'Canada'
    description: 'Description for Canada'
  Country_7:
    name: 'India'
    description: 'Description for India'
  Country_8:
    name: 'Italy'
    description: 'Description for Italy'
  Country_9:
    name: 'Spain'
    description: 'Description for Spain'
  Country_10:
    name: 'Portugal'
    description: 'Description for Portugal'
  Country_11:
    name: 'Argentina'
    description: 'Description for Argentina'
  Country_12:
    name: 'Estonia'
    description: 'Description for Estonia'
  Country_13:
    name: 'Bulgaria'
    description: 'Description for Bulgaria'
  Country_14:
    name: 'France'
    description: 'Description for France'
  Country_15:
    name: 'Belgium'
    description: 'Description for Belgium'
  Country_16:
    name: 'Austria'
    description: 'Description for Austria'
  Country_17:
    name: 'Australia'
    description: 'Description for Australia'
  Country_18:
    name: 'Japan'
    description: 'Description for Japan'
  Country_19:
    name: 'Russia'
    description: 'Description for Russia'
  Country_20:
    name: 'Slovakia'
    description: 'Description for Slovakia'
  Country_21:
    name: 'Ireland'
    description: 'Description for Ireland'

Teraz wystarczy z poziomu lini komend wywołać kolejno:


  symfony propel:build-sql
  symfony propel:build-model
  symfony propel:insert-sql
  symfony propel:data-load

ExtJS
Następnym krokiem jest pobranie najnowszej wersji (3,0) biblioteki ExtJs. Całość ściągamy z oficjalnej strony EXTa i wypakowujemy do katalogu ext.

W tym momencie warto dokonać konfiguracji pliku view.yml, tak by framework automatycznie załączył odpowiednie biblioteki js i css (zmian należy dokonoać w pliku app/frontend/config/view.yml):


stylesheets:
- ../js/ext/resources/css/ext-all.css

javascripts:
- ext/adapter/ext/ext-base-debug.js
- ext/ext-all-debug.js

Zalecane jest by kod JavaScript organizować w osobnych plikach JS, jednakże w tym wypadku kod odpowiedzialny za wyświetlanie grida umieścimy w widoku akcji index modułu Grid.

Należy pamiętać by usunąć wcześniej z akcji kod odpowiedzialny za przekierowanie do domyślnego modułu, w moim przypadku $this->forward(’default’, ‘module’);

By móc w ogóle przystąpić do pracy z komponentem grida trzeba poznać przynajmniej podstawy jego budowy i elementy składowe. Zasadniczo na GridPanel składa się 4 elementy:

  • Store - obiekt odpowiedzialny za przechowywanie danych, w tym komunikację z serwerem - w naszym przypadku JsonStore;
  • Column model - konfiguracja wyświetlania poszczególnych kolumn (m.in. opcje renderowania);
  • View - specyficzne ustawienia efektów wyświetlania;
  • Selection model - element nieobowiązkowy, odpowiada za operacje na kolumnach (zaznaczanie, pobieranie zaznaczonych) - postaram się pokazać zastosowanie tego komponentu w osobnym wpisie;

Także aby wyświetlić podstawową wersję grida potrzebujemy danych, konfiguracji kolumn, które chcemy wyświetlić i konfiguracji widoku (grid przyjmuje również dodatkowe opcje konfiguracyjne opisane szczegółowo w dokumentacji).

Poniższy kod odpowiada za wszystkie wyżej wymienione elementy (kod pliku: apps/frontend/modules/grid/templates/indexSuccess.php):


<div id="grid-example"> </div>

<?php javascript_tag(); ?>

Ext.onReady(function() {

	// komponent odpowiadający za połączenie z serwerem
 	var store = new Ext.data.JsonStore({
		root: 'result',
		totalProperty: 'total', //całwkoita ilość zwracanych danych
		idProperty: 'id',
		id: 'id',
		remoteSort: true,  // sortowanie po stronei serwera
		fields: ['id', 'name', 'description', 'created_at'],  // struktura zwracanych elementów
		url: 'grid/list',  // adres url na serwerze
		autoLoad: true  // automatyczne ładowanie danych
	});

	//struktura kolumn
	var columns = [
            {id:'name',header: 'Państwo', width: 160, sortable: true, dataIndex: 'name'},
            {header: 'Opis', width: 160, sortable: false, dataIndex: 'description'},
            {header: 'Data utworzenia', width: 100, sortable: true, dataIndex: 'created_at'}
        ];

    // obiekt grida
    var grid = new Ext.grid.GridPanel({
    	title: 'Symfony 1.2 and Ext.js example',
    	loadMask: true,   //maskowanie ładowania danych
        store: store,
        columns: columns,
        autoExpandColumn: 'name',
        height: 400,
        width: 600,
        renderTo: 'grid-example'  // element DOM do którego ma być załadowany grid
    });

});

<?php end_javascript_tag(); ?>

By całość mogła poprawnie działać nalezy utowrzyć dodatkową akcje “list”, z której komponent store będzie pobierał dane do grida (kod pliku: apps/frontend/modules/grid/actions/actions.class.php):


<?php

   public function executeList(sfWebRequest $request) {

  	 // tymczasowe dane ustawione na sztywno
  	$limit = 15;
    $page = 1;

     // pomocniczy obiekt odpowiedzialny za stronicowanie
  	$pager = new sfPropelPager('Country', $limit);

    $pager->setPage($page);
    $pager->init();

    $result = array();

    foreach($pager->getResults() as $country) {

    	$result[] = $country->toArray(BasePeer::TYPE_FIELDNAME);
    }

     // sformatowane dane są zwracane do grida
    return $this->renderText(json_encode(array(
    	'total'		=> $pager->getNbResults(),
    	'result'	=> $result
    )));
  }

?>

JSON (JavaScript Object Notation) jest podstawowym formatem danych używanym w aplikacjach opartych o framework ExtJS. Dostęp do danych w formacie JSON jest łatwiejszy i szybszy z poziomu języka JavaScript niż dostęp do tych samych danych w formacie XML

W tym momencie można sprawdzić działanie kodu wywołując w przeglądarce adres:
http://localhost/nazwa-aplikacji/web/grid

Powinniśmy ujrzeć grida podobnego do tego przedstawionego na rysunku poniżej:

Obsługa stronicowania i sortowania

Obecna postac grida jest dość niewygodna, o ile w przypadku kilkunastu rekordów wyświetlanie ich na jednej stronie nie jest zbyt kłopotliwe, to w momencie gdy baza zacznie się rozrastać ładowanie wszystkich danych i operowanie na nich staje się niemal niemożliwe.

Za stronicowanie po stronie ExtJS odpowiada component: PagingToolbar. Jego kluczowym elementem jest zdefiniowany wcześniej komponent store. Kod odpowiedzialny za stronicowanie widoczny jest poniżej:


	paging = new Ext.PagingToolbar({
		pageSize: 10,  // ilość elementów
		store: store,  // obiekt magazynujący dane
		displayInfo: true,  //wyśweitlanie dodatkowych informacji
		displayMsg: 'Wyświetlanie państw {0} - {1} z {2}',   // komunikaty
		emptyMsg: "Lista państw jest pusta"  

	});

         //małą modyfikacja kodu grida
	var grid = new Ext.grid.GridPanel({
        	...
		bbar: paging,   // ustawiamy dolny panel grida
		...
	});

By stronicowanie i sortowanie działało prawidłowo modyfikacji wymaga akcja list, w której musimy zbudować odpowiednie zapytanie na podstawie parametrów żądania.

Wywołanie akcji sortowania lub stronicowania w gridzie powoduje, ze tworzone jest nowe żądanie pobrania danych z akcji list z parametrami: dir (kierunek sortowania ASC/DESC), limit, sort (nazwa pola po którym sortujemy), start (offset)

Modyfikacji wymaga też sama akcja executeList - musimy dodać do Niej obsługę stronicowania i sortowania po wybranej kolumnie:


<?php

  public function executeList(sfWebRequest $request) {

  	 // dane pobierane z żądania
  	$limit = $request->getParameter('limit', 10);
    $page = $request->hasParameter('start') ? $request->getParameter('start')/$limit+1 : 1;
    $dir = $request->getParameter('dir', 'asc');
    $column = strtolower($request->getParameter('sort', 'name'));

     # warunki dla sortowania
    $c = new Criteria();
   	if (in_array($column, CountryPeer::getFieldNames(BasePeer::TYPE_FIELDNAME))) {

   		$column = CountryPeer::translateFieldName($column, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_COLNAME);
   		if ($dir == "ASC") {

   			$c->addAscendingOrderByColumn($column);
   		} else {

   			$c->addDescendingOrderByColumn($column);
   		}
   	}

     # pomocniczy obiekt odpowiedzialny za stronicowanie
  	$pager = new sfPropelPager('Country', $limit);

  	 # kryteria dla pagera
  	$pager->setCriteria($c);
    $pager->setPage($page);
    $pager->init();

    $result = array();

    foreach($pager->getResults() as $country) {

    	$result[] = $country->toArray(BasePeer::TYPE_FIELDNAME);
    }

     # sformatowane dane są zwracane do grida
    return $this->renderText(json_encode(array(
    	'total'		=> $pager->getNbResults(),
    	'result'	=> $result,
    	'page'		=> $page
    )));
  }

?>

Teraz wystarczy tylko odświeżyć stronę i sprawdzić czy wszystkie zmiany zostały poprawnie naniesione - całość powinna wyglądać mniej więcej tak:

W przypadku gdy nie wszystkie nowe elementy wyświetlają się poprawnie zalecane jest czyszczenie cache komendą symfony cc (alias dla clear-cache). Tego typu operację należy również przeprowadzić w przypadku np: generowania klas modeli czy formularzy, instalacji pluginów, a w szczególności gdy zmieniamy pliki konfiguracyjne zapisane w plikach z rozszerzeniem YML.

0 Responses to “Symfony 1.2 i ExtJS - wyświetlanie danych w gridzie”


  1. No Comments

Leave a Reply

Podgląd komentarza:




About me:

  • PHP programmer
  • Symfony developer
  • Zend framework developer

Categories: