Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: batman, dodany: 14.02.2011 08:00, tagi: javascript, jquery

Wraz z nadejściem jQuery 1.5, programiści otrzymali do swoich rąk szereg funkcjonalności usprawniających pracę z AJAXem. Jedną z nich są obiekty wstrzymane (deferred objects). W dużym skrócie dzięki deferred object możemy przypisać callback do AJAXowego requestu w dowolnym momencie, a nie jak to miało miejsce do tej pory, w momencie tworzenie requestu. Co więcej nie musimy ograniczać się tylko do jednej funkcji zwrotnej.

Jak to się robiło kiedyś…

Przed nastaniem jQuery 1.5 typowy request AJAXowy wyglądał następująco.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
        <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.5.js"></script>
        <script>
        $(document).ready(function() {
        	$.ajax({
        		url: "ajax.php",
        		type: "POST",
        		success: function(data) {
        			$("#content").append(data);
        		}
        	});
        });
        </script>
    </head>
    <body>
        <div id="content"></div>
    </body>
</html>

W efekcie działania tego nieco uproszczonego przykładu, do diva o identyfikatorze content dodana zostanie zawartość odpowiedzi serwera. Jeśli chcielibyśmy wykonać więcej operacji na zwróconych przez serwer danych oraz uzależnić niektóre operacje od wyniku działania innych funkcji, nieźle byśmy się napocili.

A jak to się robi dziś?

Na szczęście z pomocą przychodzi nam najnowsza odsłona jQuery. W wersji 1.5 opisany powyżej problem rozwiążemy w bardzo prosty sposób

$(document).ready(function() {
	var req = $.ajax({
		url: "ajax.php",
		type: "POST",
		success: function(data) {
			$("#content").append(data);
		}
	});

	/* dużo innych operacji, które muszą się wykonać niezależnie od AJAXa */

	req.success(function(data) {
		/* korzystamy z danych przetworzonych powyżej */

		// zrób coś z danymi z serwera
		$("#content").append(data);
	});
});

Drugi callback success wykona się dopiero w momencie, gdy zakończą się operacje znajdujące się bezpośrednio przed nim. Zmienna data zawierać będzie dokładnie te same dane, co w przypadku pierwszej funkcji callback. W podobny sposób możemy dodać callbacki error oraz complete.

Magia?

Obiekty wstrzymane (deferred object) wykorzystują wzorzec Promises/A. Zakłada on istnienie bytu nazwanego obietnicą (promise), który określa potencjalną wartość zwróconą w wyniku działania jakiejś operacji. Obietnica może znajdować się w jedenym z trzech stanów – niespełniona, spełniona, zakończona niepowodzeniem, przy czym zmiana stanu może nastąpić tylko raz: z niespełnionej na spełnioną lub z niespełnionej na zakończoną niepowodzeniem. W momencie gdy obietnica znajdzie się w stanie spełniona lub zakończona błędem, zmiana stanu nie może już mieć miejsca. Dzięki takiemu zachowaniu obietnic, dane zwrócone do callbacka pozostają niezmienne niezależne od miejsca, w którym do tych danych się odwołujemy.

W celu umożliwienia korzystania z wspomnianej przed chwilą funkcjonalności, jQuery zostało wyposażone w obiekt Deferred. To co się dzieje “behind the scenes”, wygląda łudząco podobnie do typowego wywołania metody ajax.

function funkcja1()
{
	var dfd = $.Deferred();
	dfd.resolve("ok");
	return dfd.promise();
}

function funkcja2()
{
	var dfd = $.Deferred();
	dfd.reject("my bad");
	return dfd.promise();
}

$.when(funkcja1(), funkcja2())
	.fail(function() {
		console.debug("co najmniej jedna funkcja sie wywaliła");
	})
	.done(function() {
		console.debug("wszystkie funkcje są ok");
	})
	.then(function() {
		console.debug("ok");
	}, function() {
		console.debug("coś poszło nie tak");
	});

Powyższy przykład nie jest może zbyt funkcjonalny, ale idealnie pokazuje zasadę działania obiektu Deferred. Metoda when przyjmuje jako argumenty dowolną ilość funkcji, których stan chcemy monitorować. Do obiektu zwróconego przez metodę when dodajemy handlery uruchamiane w momencie gdy wszystkie funkcje zakończą swoje działanie sukcesem (handler dome) lub co najmniej jedna z nich zakończy się niepowodzeniem (fail). Handler then przyjmuje dwa parametry. Pierwszym jest funkcja wykonywana w przypadku sukcesu, drugim funkcja wykonywana w momencie niepowodzenia.

Skąd metoda $.when wie czy monitorowane funkcje zakończyły się sukcesem lub porażką? Informuje ją o tym obiekt Deferred, który zwraca opisaną wcześniej obietnicę. Nim obietnica zostanie zwrócona, należy ustawić jej stan przy pomocy jeden z dwóch metod – resolve dla poprawnego sukcesu oraz reject dla porażki.

Słowo końcowe

Przedstawione powyżej informacje na temat deferred objects to tylko wierzchołek góry lodowej, której cały obraz ujrzycie po lekturze dokumentacji. Warto również zapoznać się z tym wpisem. Znajdziecie w nim szereg interesujących przykładów.

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

Autor wpisu: batman, dodany: 09.02.2011 08:00, tagi: php

Przez ostatnie trzy części “PHP w chmurze” poznawaliśmy Azure Storage, czy mechanizmy przechowywania danych w Windows Azure. Dla przypomnienia – były to bloby, kolejki oraz tabele. Ponieważ we wspomnianych wpisach skupiłem się na wyjaśnieniu jak one działają, a nie na ich praktycznych zastosowaniach, mechanizmy te mogły wydawać się nieco oderwane od rzeczywistości. Dzisiaj, zainspirowany jednym z tutoriali z serwisu Windows Azure for PHP, postanowiłem napisać jego nieco ulepszoną wersję.

Budowa aplikacji

Aplikacja nad którą będziemy pracować, nie będzie wiele robiła. Jej jedynym zadaniem będzie wysyłanie maili. W prawdziwym życiu, aplikacja taka stanowiłaby część większej całości. Aby nie zaciemniać obrazu, zastosowałem kilka skrótów, które w produkcyjnych aplikacjach nie powinny mieć miejsca.

Aplikacja będzie podzielona na dwie części – web role oraz worker role. Gwoli przypomnienia wyjaśnię, że rola typu web jest typową aplikacją internetową, jaką wszyscy znamy i lubimy – posiada graficzny interfejs użytkownika, można się do niej dostać z dowolnego miejsca Internetu, a odebrane dane zapisuje w celu dalszej ich obróbki. Z kolei rola typu worker, to tania siła robocza, która zajmuje się przetwarzaniem danych. Idealnie nadaje się do wykonywania zadań, mogących spowodować spadek wydajności roli web. W pewnym sensie workera można przyrównać do usługi, która działa w tle zawsze gotowa do pracy.

W naszej aplikacji rola web będzie formularzem odbierającym dane od użytkownika i zapisującym je w Azure Storage, a worker na ich podstawie będzie wysyłał emaile. Dlaczego wysyłką maili ma się zajmować worker, a nie web? Z bardzo prostego powodu. Jeśli serwer pocztowy nie będzie dostępny, wówczas rola web (widoczna dla użytkownika) przestanie działać lub w najlepszym wypadku będzie zgłaszała błąd w po wysłaniu formularza. Worker z kolei, mimo iż napotka ten sam problem, zgłosi go po cichu (z dala od wścibskich oczu). W ten sposób użytkownik cieszy cię, że strona działa szybko, dane nie są tracone, a my wysyłamy armię wyszkolonych małp do naprawienia problemu.

Do komunikacji między rolami, wykorzystane zostaną kolejki, załączniki będą przechowywane jako bloby, a tabele posłużą nam jako pojemnik na informacje wysłane przez użytkownika.

Na koniec warto wspomnieć, iż formularz, walidację danych oraz wysyłkę maili zleciłem Zend Frameworkowi. Szybko, elegancko, bez marnowania czasu.

Tworzenie projektu

Wiemy już jakie role będą nam potrzebne (jedna web i jedna worker) oraz z jakich mechanizmów przechowywania danych będziemy korzystać, czas rozpocząć prace nad aplikacją. Zaczniemy od utworzenia nowego projektu w Eclipse (instalację środowiska znajdziecie w pierwszym wpisie serii).

Z menu File wybieramy opcję New –> Project, a następnie Windows Azure PHP Project.

azure1

Jako nazwę projektu wpisujemy azuremail i klikamy przycisk Next. W kolejnym oknie w polu Role name wpisujemy web1, zaznaczamy checkbox Windows Azure Data Storage oraz ponownie klikamy przycisk Next

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

Autor wpisu: matipl, dodany: 08.02.2011 09:01, tagi: php

Zend Framework Wstępnie chciałem napisać o lokalizacji, aby zaprezentować Wam jak można wykorzystać application.ini zamiast tworzyć własne zasoby (resource). Ale z drugiej strony może nie każdy wie jakie to proste w dzisiejszych czasach zrobić serwis wielojęzyczny z tłumaczeniem nie tylko statycznych napisów, ale również nawigacji czy lokalizacją kwot.

Do tłumaczeń korzystam z gettext (pliki .mo i .po). Moje pliki językowe znajdują się w project/languages i wygląda to tak:

matipl@host:~/project/languages$ ls
en_GB.mo  en_GB.po  pl_PL.mo  pl_PL.po

Gdy mamy stworzone własne tłumaczenia pora na skonfigurowanie zasobu translate w application.ini:

resources.translate.registry_key   = "Zend_Translate"
resources.translate.adapter        = "gettext"
resources.translate.content        = APPLICATION_PATH "/../languages/"
resources.translate.options.scan    = "filename"
resources.translate.disableNotices = false
resources.translate.options.logUntranslated = false
resources.translate.locale        = "pl_PL"

W ten oto sposób możemy już korzystać z plików lokalizacyjnych opartych o Zend_Translate (skonfigurowany zasób znajduje się w Zend_Registry::get(‘Zend_Translate’)). Niestety nie wie on z jakiej wersji językowej (pl czy en) chcemy skorzystać.

W tym celu stworzyłem plugin Zextend_Controller_Plugin_Locale, który pobiera wybrany (lub domyślny) język użytkownika i konfiguruje Zend_Translate. Dodatkowo mówimy widokowi i nawigacji, że ma się wspierać przez Zend_Translate z tym konkretnym językiem, który skonfigurowaliśmy.

class Zextend_Controller_Plugin_Locale extends Zend_Controller_Plugin_Abstract
{

    public function routeShutdown(Zend_Controller_Request_Abstract $request)
    {
        $view = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('view');
        $locale = new Zend_Locale(Zextend_Lang::getActiveLang());
        Zend_Registry::set('Zend_Locale', $locale);

        $translate = Zend_Registry::get('Zend_Translate');
        $translate->setLocale($locale);

        $view->getHelper('translate')->setTranslator($translate);
        $view->navigation()->setTranslator($translate);

        Zend_Form::setDefaultTranslator($translate);

        Zend_Registry::set('Zend_Translate', $translate);

    }

}

Na koniec wystarczy włączyć nasz plugin w application.ini:

resources.frontController.plugins.locale = "Zextend_Controller_Plugin_Locale"

Teraz możemy już swobodnie korzystać z tłumaczeń na naszej stronie, np. w widoku wywołując:

<h2><?php echo $this->translate('Contact') ?></h2>

Dla słowa „Contact” musimy mieć oczywiście odpowiedni wpis w plikach *.po. Dla wersji PL:

msgid "Contact"
msgstr "Kontakt"

Po stworzeniu tłumaczenia pamiętajmy o wygenerowaniu pliku .mo komendą: msgfmt -o pl_PL.mo pl_PL.po.

Na koniec dodam, że w danych czasach sporo rzeczy robiłem poza application.ini. Chociażby przeciążałem zasób Db, aby wprowadzić SET NAMES UTF8, czy też w index.php wpisywało się dyrektywy PHP. A obecnie?

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

Autor wpisu: Śpiechu, dodany: 07.02.2011 21:41, tagi: mysql, php, zend_framework

Kontynuujemy temat zapytań z poprzedniej części. Punktem wyjścia będzie dla nas tablica wyników zapytania dorzuconego przeze mnie LEFT JOINA (w ramach aktualizacji wpisu na samym dole poprzedniej części). Aby nieco utrudnić dorzuciłem kolejnych dwóch powszechnie znanych idoli młodzieży: Antoniego Macierewicza i Stefana Niesiołowskiego, którzy dla odmiany nie będą mieli swoich ksywek. Ponadto dodałem drugą ksywkę towarzyszowi Tuskowi znanemu często na forach Onetu jako Rudy Oszust.

Reasumując: mamy polityka z dwoma ksywkami, jednego z jedną i dwóch bez ksywek. Dążymy do tego aby wyświetlić na liście rozwijanej formularza ich wszystkich. Zarówno oryginalne imię i nazwisko jak i ksywkę. Co jednak zrobić, skoro ksywka i oryginalne imię i nazwisko posiadają to samo id w bazie? Zamienimy je na unikatowe poprzez dodanie jakiegoś dodatku po oryginalnym id, np. 5–1, 5–2 itd.

Najpierw jednak stworzymy super prosty formularz zawierający pole wyboru typu select i przycisk zatwierdzający zmiany. Potrzebujemy klasy dziedziczącej po Zend_Form. Ja pracując w Zendzie zazwyczaj formularze wrzucam do katalogu forms równoległego do controllers, models itd.

class PolitycyForm extends Zend_Form {
 
   public function init() {
      $this->setMethod('post');
      // gdzie ma zostac wyslany formularz
      $this->setAction('/index/index');
 
      // tutaj wstawić zapytanie
      // z aktualizacji wpisu poprzedniej czesci
 
      $stmt = $select1->query();
      $rowset = $stmt->fetchAll();
 
      $wyniki = array();
      $counter = 1;
 
      foreach ($rowset as $row) {
         // tymczasowo kluczami staja sie wyniki zapytania,
         // a wartosciami id i kolejny numer
         $wyniki[$row['imie_nazwisko']] = $row['id'] . '-' . $counter++;
         // sprawdzamy czy ten ktos ma ksywke,
         // jezeli tak to dorzucamy do wynikow
         if ($row['ksywka'] != null) {
            $wyniki[$row['ksywka'] . ' (' . $row['imie_nazwisko'] . ')'] = $row['id']. '-' . $counter++;
         }
      }
      // nastepnie wszystko to sortujemy po kluczach
      // do poprawnego posortowania polskich znakow
      // uzywamy funkcji setlocale
      setlocale(LC_COLLATE, 'pl_PL.utf8');
      ksort($wyniki, SORT_LOCALE_STRING);
      // i zamieniamy miejscami klucze z wartosciami
      $wyniki = array_flip($wyniki);
 
      $formElement = new Zend_Form_Element_Select('politycy');
      $formElement->setRequired(true)
         // blokujemy tworzenie domyslnego walidatora
         // sprawdzajacego czy wynik jest w formie tablicy
         ->setRegisterInArrayValidator(false)
         ->setLabel('Wybierz swojego ulubionego polityka')
         ->setMultiOptions($wyniki)
         // sprawdzamy czy ktos nie robi psikusa
         ->addValidator(new Zend_Validate_Regex('/^[0-9]+\-[0-9]+$/'));   
      $this->addElement($formElement);
      // dodajemy pole typu submit
      $this->addElement('submit','wybierz');
   }   
}

Po wszystkich zabiegach tablica $wyniki przekazywana do obiektu Zend_Form_Element_Select posiada następującą strukturę:

array(7) {
  ["3-7"] => string(18) "Antoni Macierewicz"
  ["1-3"] => string(11) "Donald Tusk"
  ["2-6"] => string(31) "Jareczek (Jarosław Kaczyński)"
  ["2-5"] => string(20) "Jarosław Kaczyński"
  ["1-4"] => string(25) "Rudy Oszust (Donald Tusk)"
  ["1-2"] => string(27) "Słońce Peru (Donald Tusk)"
  ["4-8"] => string(20) "Stefan Niesiołowski"
}

Teraz pozostaje nam odebrać formularz. Żeby zbytnio nie komplikować dane odbierzemy w kontrolerze IndexController w akcji indexAction(). Na przykład tak:

$politycyForm = new PolitycyForm();
if ($this->_request->isPost()) {
   $dane = $this->getRequest()->getPost();
   if ($politycyForm->isValid($dane)) {
      // wyrzucamy szmelc po wlasciwym identyfikatorze
      $filtr = new Zend_Filter_PregReplace(
         array('match' => '/\-[0-9]+/',
               'replace' => ''));
      $przefiltrowane = $filtr->filter($dane['politycy']);
 
      // wykonujemy dzialania na bazie danych
      // co wykracza poza ramy tego wpisu
 
      // zakladamy, ze istenieje akcja panel-uzytkownika
      return $this->_redirect('/index/panel-uzytkownika');
   }
   else {
      // jezeli formularz nie przechodzi walidacji
      // to zostaje uzupelniony o wprowadzone poprzednio dane
      $politycyForm->populate($dane);
   }
}
// wyswietlamy formularz
$this->view->politycy = $politycyForm;

Możecie zapytać po co nam ten _redirect. Otóż zabezpiecza nas przed ponownym wyświetleniem użytkownikowi formularza i przed ewentualnym ponownym wysłaniem danych.

Jeżeli za szybko z czymś pojechałem, składać zażalenia w komentarzach :-)

Autor wpisu: batman, dodany: 07.02.2011 08:00, tagi: javascript

Podczas tworzenia niewielkich formularzy logowania, bardzo często można natknąć się na projekt graficzny zakładający domyślne wartości w polach login oraz hasło (zamiast etykiet obok pól) informujące użytkownika o rodzaju danych jakie ma wprowadzić. O ile login nie stanowi żadnego problemu, tak w przypadku hasła jest to nie lada wyzwanie.

Jednym z możliwych rozwiązań jest zastosowanie ukrytych pól tekstowych. Technika ta polega na wstawieniu bezpośrednio po polu typu password, pola tekstowego zawierającego domyślny tekst. Następnie przy pomocy JavaScript sterujemy widocznością hasła oraz maski.

<form id="formularz" method="post" action="">
	<div>
		<input type="text" name="login" id="login" value="Wpisz login" />
	</div>
	<div>
		<input type="password" name="password" id="password" style="display: none;" />
		<input type="text" name="password_mask" id="password_mask" value="Wpisz hasło" />
	</div>
	<div>
		<input type="submit" name="btn_signin" id="btn_signin" value="Zaloguj" />
	</div>
</form>

Dzięki jQuery całą podmianę można zmieścić zaledwie w kilku wierszach. Nic nie stoi na przeszkodzie, aby skorzystać z dowolnej biblioteki JavaScript lub nawet z “czystego” języka. Kod odpowiedzialny za ukrywanie/pokazywanie zaślepki wygląda następująco (w przykładzie pominąłem login).

$(document).ready(function() {
	$("#password_mask").focus(function() {
		$(this).hide();
		$("#password").show().focus();
	});

	$("#password").blur(function() {
		if($(this).val().length == 0) {
			$(this).hide();
			$("#password_mask").show();
		}
	});
});

Najpoważniejszą wadą zaprezentowanego rozwiązania jest jego podatność na błędy JavaScript. Dowolny błąd na stronie, np. związany ze statystykami lub innym zewnętrznym skryptem, spowoduje, że tak przygotowany formularz logowania niestety nie zadziała. Najlepszym zabezpieczeniem przed taką sytuacją jest wstrzyknięcie maskującego pola tekstowego oraz ukrycie pola typu password przez JavaScript. W ten sposób nawet w przypadku problemów z JavaScriptem, formularz logowania pozostanie sprawny. Ostatecznie cały kod będzie wyglądał przybierze następującą postać.

<form id="formularz" method="post" action="">
	<div>
		<input type="text" name="login" id="login" value="Wpisz login" />
	</div>
	<div>
		<input type="password" name="password" id="password" />
	</div>
	<div>
		<input type="submit" name="btn_signin" id="btn_signin" value="Zaloguj" />
	</div>
</form>
<script type="text/javascript">
$(document).ready(function() {
	$("<input type=\"text\" />")
		.attr("name", "password_mask")
		.attr("id", "password_mask")
		.val("Wpisz hasło")
		.insertAfter("#password");

	$("#password_mask").focus(function() {
		$(this).hide();
		$("#password").show().focus();
	});

	$("#password").hide().blur(function() {
		if($(this).val().length == 0) {
			$(this).hide();
			$("#password_mask").show();
		}
	});
});
</script>

Autor wpisu: Kamil, dodany: 06.02.2011 15:06, tagi: css

Ostatnio natknąłem się na bardzo fajną rzecz – możliwość wysyłania pliku CSS w nagłówku HTTP. Daje to bardzo łatwą możliwość ukrycia swoich stylów CSS przed niedoświadczonymi developerami, niepożądanymi i wścibskimi oczami. Oczywiście tak przesłane style CSS do strony nie stanowią większego problemu do obejścia dla doświadczonego developera (Firebug), niemniej nie każdemu będzie chciało się analizować [...]

Autor wpisu: matipl, dodany: 06.02.2011 13:26, tagi: php

Zend FrameworkW październiku 2010 roku wspominałem Wam, że wraz z pojawieniem się Zend Frameworka w wersji 1.11.0 ułatwiono nam dostosowanie aplikacji opartej o ZF dla moblinych przeglądarek.

Dzięki pomocy Raphaela w frameworku pojawiły się m.in. Zend_Http_UserAgent oraz Zend_View_Helper_UserAgent. Dzisiaj chciałbym szybko Wam pokazać jak w łatwy sposób wykryć czy mamy doczynienia z mobilną przeglądarką (zbliża się wersja bilancio dla mobilnych).

WURFL, czyli powtarzamy kroki z manuala

Zasób UserAgent korzysta z zewn. biblioteki – WURFL (Wireless Universal Resource File). Ściągamy wersję 1.1 i rozpakowujemy w dowolnym miejscu. W naszej aplikacji tworzymy /library/wurfl-php-1.1 i kopiujemy tam katalog WURFL (reszty ze ściągniętej paczki tutaj nie potrzebujemy).

Aby zakończyć ten krok musimy jeszcze skopiować plik z informacjami o urządzeniach mobilnych. Plik wurfl-latest.zip znajduje się w paczce, w ścieżce tests/resources/. Tworzymy katalog w naszej aplikacji /data/wurfl oraz /data/wurfl/cache (w tym miejscu będzie przechowywał zawartość pliku zip). Do /data/wurfl kopiujemy plik wurfl-latest.zip (kopiujemy również web_browsers_patch.xml). W skrócie (project to nazwa naszego projektu ZF):


wget "http://downloads.sourceforge.net/project/wurfl/WURFL%20PHP/1.1/wurfl-php-1.1.tar.gz?r=&amp;ts=1296984042&amp;use_mirror=sunet" -O wurfl-php-1.1.tar.gz

tar -xvf wurfl-php-1.1.tar.gz

mkdir project/library/wurfl-php-1.1

mkdir project/data

mkdir project/data/wurfl

mkdir project/data/wurfl/cache

chmod a+w project/data/wurfl

cp -R wurfl-php-1.1/WURFL project/library/wurfl-php-1.1/

cp wurfl-php-1.1/tests/resources/wurfl-latest.zip project/data/wurfl

cp wurfl-php-1.1/tests/resources/web_browsers_patch.xml project/data/wurfl

Konfiguracja WURFL

Pliki mamy skopiowane, skonfigurujmy teraz samego WURFL. Zróbmy to wg tutorialu:

<?php
$resourcesDir            = dirname(__FILE__) . '/../../data/wurfl/';

$wurfl['main-file']      = $resourcesDir  . 'wurfl-latest.zip';
$wurfl['patches']        = array($resourcesDir . 'web_browsers_patch.xml');

$persistence['provider'] = 'file';
$persistence['dir']      = $resourcesDir . '/cache/';

$cache['provider']       = null;

$configuration['wurfl']       = $wurfl;
$configuration['persistence'] = $persistence;
$configuration['cache']       = $cache;

Konfiguracja chyba jest jasna? Wskazujemy głównie miejsca plików, które wcześniej kopiowaliśmy.

Na koniec konfiguracja projektu Zend Framework. Jak ostatnio wspominałem we wpisie Zend Framework i Symfony – subiektywnie, obecnie jesteśmy w stanie zrobić większość w application.ini. W takim razie dodajmy do niego informację, że chcemy skorzystać z UserAgent i powiedzmy, gdzie umieściliśmy WURFL, który jest odpowiedzialny za „rozszyfrowanie” danych o urządzeniu mobilnym:

resources.useragent.wurflapi.wurfl_api_version = "1.1"
resources.useragent.wurflapi.wurfl_lib_dir = APPLICATION_PATH "/../library/wurfl-php-1.1/WURFL/"
resources.useragent.wurflapi.wurfl_config_file = APPLICATION_PATH "/configs/wurfl-config.php"

W tym momencie zasób UserAgent już działa i możemy z niego korzystać.

Plugin

Nasza cała aplikacja ma uwzględniać urządzenia mobilne dlatego stworzymy plugin, który w przypadku wersji mobilnej zmieni nam layout na mobilny:

class Zextend_Controller_Plugin_Mobile extends Zend_Controller_Plugin_Abstract
{

    public function routeShutdown(Zend_Controller_Request_Abstract $request)
    {
        $bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');
        $userAgent = $bootstrap->getResource('useragent');
        $device = $userAgent->getDevice();
        if($device->getType() == 'mobile') {
            Zend_Layout::getMvcInstance()->setLayout('mobile');
        }
    }
}

$device udostępnia nam nie tylko typ urządzenia (getType()), ale również jego szerokość ekranu (getPhysicalScreenWidth()); W bilancio będzie to po prostu przekierowanie na osobą domenę (pobierana z application.ini z app.mobileUrl), ponieważ udostępniona funkcjonalność będzie różnić się od standardowej wersji:

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

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