Niezalogowany [ logowanie ]
Subskrybuj kanał ATOM Kanał ATOM

Autor wpisu: sokzzuka, dodany: 17.06.2010 22:51, tagi: php

W poprzednich częściach tutorialu omawiałem jak stworzyć szkielet aplikacji webowej w Pythonie przy użyciu mod_wsgi oraz jak wykonywać proste operacje na bazie (select, insert). Dzisiejszy wpis będzie o odczytywaniu informacji z plików w popularnych formatach XML i CSV.

Jak zaraz zobaczycie w obu językach wygląda to podobnie, najpierw parsowanie CSV w Pythonie oraz wyświetlanie HTML-owej tabelki:

import csv
 
def test(path):
    sFile = path + 'files/sample.csv'
    oReader = csv.reader(open(sFile), delimiter=',', quotechar='"')
    aResult = []
    for row in oReader:
        aResult.append(row)
 
    sResult = '<table><tbody>'
    for row in aResult:
        sResult += '<tr>'
        for cell in row:
            sResult += '<td>' + cell + '</td>'
        sResult += '</tr>'
 
    sResult += '</tbody></table>'
    return sResult

Ekwiwalent w php

<?php
$sPath = 'res' . DIRECTORY_SEPARATOR . 'csv' . DIRECTORY_SEPARATOR . 'sample.csv';
 
$rFile = fopen( $sPath, 'r' );
$aResult = array();
$mResult = array();
do {
    $mResult = fgetcsv( $rFile );
    $aResult[] = $mResult;
} while ($mResult !== false);
 
$iRows = count($aResult);
unset ($aResult[$iRows-1]);
?>
<table>
    <tbody>
<?php foreach ($aResult as $row): ?>
            <tr>
        <?php foreach ($row as $cell): ?>
                    <td><?php echo $cell ?></td>
            <?php endforeach; ?>
                    </tr>
            <?php endforeach; ?>
    </tbody>
</table>

Jak widać kod w Pythonie jest troszkę krótszy ze względu na większą abstrakcje – w PHP można by osiągnąć ten sam efekt poprzez opakowanie funkcji fgetcsv w obiekt implementujący interfejs Iterator.

Naszym kolejnym obiektem zainteresowanie będzie parsowanie plików XML, za zadanie weźmiemy sobie wyciągnięcie z pliku RSS wszystkich tytułów wpisów, będziemy korzystać z pasera typu DOM.

Kod pythonowy:

def test(path):
    from xml.dom.minidom import *
 
    sFile = path + 'files/rss.xml'
    # @type oDom xml.dom.minidom.Document
    oDom = parse(sFile)
 
    lTitles = oDom.getElementsByTagName('title')
    output = ''
 
    output += "<h1>Titles</h1>"
    output += "<ul>"
 
    for node in lTitles:
        output += "<li>" 
        output += node.childNodes[0].nodeValue
        output += "</li>"
    output += "</ul"
    return output

Kod w PHP:

$sPath = 'res'.DIRECTORY_SEPARATOR.'xml'.DIRECTORY_SEPARATOR.'rss.xml';
 
$oDom = new DOMDocument();
$oDom->preserverWhiteSpace = false;
$oDom->load($sPath);
 
$oTitles = $oDom->getElementsByTagName('title');
echo "<h1>Titles</h1>";
echo "<ul>";
foreach($oTitles as $title){
    $sTitle = $title->nodeValue;
    echo "<li>",$sTitle,"</li>";
}
echo "</ul>";

Widać kilka zasadniczych różnic w obu językach, po pierwsze aby parsować dokument XML w php należy użyć metody DOMDocument::load, natomiast w Pythonie mamy metode parse modułu minidom.

Po drugie w PHP jeżeli mamy noda którego jedyną zawartością jest tekst, to możemy się do niego dostać przez właściwość nodeValue, natomiast Pythonowy parser widzi nod z tekstem wewnątrz jak nod z dzieckiem, które to jest nodem tekstowym.

Tym wpisem prawdopodobnie zakończę serię “Aplikacje webowe w Pythonie dla programistów PHP”, mam nadzieje, że udało mi się zachęcić Was do zabawy z tym ciekawym językiem i spojrzenia z innej perspektywy na znane Wam zapewne zagadnienia w programowaniu.

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

Autor wpisu: batman, dodany: 16.06.2010 18:00, tagi: zend_framework

Zend Frameworku oferuje kilka różnych routerów. Jednym z ciekawszych i zarazem najtrudniejszych w zastosowaniu jest Zend_Controller_Router_Route_Hostname. Router ten w połączeniu z modułami daje ogromne możliwości. Można na przykład stworzyć kilka modułów aplikacji, na które wskazują odpowiadające im subdomeny.

Router można zdefiniować na dwa sposoby – w pliku konfiguracyjnym lub w pliku Bootstrap. W tym przypadku preferuję drugą metodę, co nie oznacza, że pierwsza jest zła. W przypadku korzystania z pliku Bootstrap należy pamiętać o jednej rzeczy. Metoda musi nazywać się _initRouter. W przeciwnym wypadku nasz router nie będzie działał. Niestety nie udało mi się ustalić dlaczego tak się dzieje.

Zend_Controller_Router_Route_Hostname można stosować jako osobny router, jednak wówczas nasza aplikacja nie będzie w stanie dotrzeć do innych akcji niż domyślne. Dzieje się tak dlatego, ponieważ każda ścieżka będzie pasowała do naszego routera. Aby temu zapobiec, należy użyć innego routera – Zend_Controller_Router_Route_Chain. Na szczęście jego wykorzystanie jest banalne i sprowadza się do wywołania metody chain na obiekcie routera hostname.

W praktyce wygląda to następująco:

protected function _initRouter()
{
	$this->bootstrap('frontController');
	$front = $this->getResource('frontController');
	$router = $front->getRouter();

	$hostRouter = new Zend_Controller_Router_Route_Hostname(
		':module.przykladowa-domena.pl',
		array(
			'module' => ''
		)
	);

	$defaultRouter = new Zend_Controller_Router_Route(
		':controller/:action/*',
		array(
			'controller' => 'index',
			'action' => 'index'
		)
	);
	
	$router->addRoute('subdomain', $hostRouter->chain($defaultRouter));
}

Do poprawnego działania, router potrzebuje dwóch parametrów. Pierwszym jest adres, który zostanie przetłumaczony na moduł, drugim domyślna nazwa modułu jaki zostanie użyty w helperze widoku url. Jeśli jako moduł zdefiniujemy pusty string, wówczas helper url zwróci nam adres wskazujący na domenę, bez żadnej subdomeny.

echo $this->url(array(), 'subdomain');
// wyświetli http://przykladowa-domena.pl

Jeśli jako moduł domyślny ustawiony zostanie jakiś string, np admin, wówczas helper wyświetli http://admin.przykladowa-domena.pl. Do helpera możemy przekazać nazwę modułu. Wówczas wygenerowany zostanie adres z odpowiednią subdomeną.

echo $this->url(array('module' => 'jakis-modul'), 'subdomain');
// wyświetli http://jakis-modul.przykladowa-domena.pl

Drugim zdefiniowanym routerem jest Zend_Controller_Router_Route. Dzięki niemu wszystkie parametry przekazane w ścieżce zostaną przetłumaczone na nazwę kontrolera, nazwę akcji oraz dodatkowe parametry.

Autor wpisu: batman, dodany: 16.06.2010 12:46, tagi: zend_framework

Dwa dni temu wydana została nowa wersja Netbeans, oznaczona numerem 6.9. W sumie nie byłoby nic w tym ciekawego, gdyby nie fakt, że do IDE dodana została obsługa Zend Frameworka.

Zanim będziemy mogli skorzystać z możliwości tworzenia projektu ZF, musimy skonfigurować nasze środowisko. Konfiguracja wygląda bardzo podobnie jak w przypadku dodawania ścieżek do zmiennych środowiskowych. Musimy wskazać katalog, w którym znajduje się Zend Framework oraz katalog z plikiem wykonywalnym php.

Po poprawnym skonfigurowaniu IDE, można stworzyć projekt PHP w oparciu o Zend Framework. Projekt będzie posiadał standardową strukturę katalogów. Niestety dodawanie elementów frameworka do projektu (np formularze, akcje, czy moduły) sprowadza się do wpisywania tych samych komend, z których byśmy korzystali w wierszu poleceń. Jedyną różnicą jest to, że Netbeans podpowiada nam składnię poleceń.

Prezentację możliwości nowej wersji Netbeans możecie obejrzeć na oficjalnej stronie tego środowiska.

Autor wpisu: sokzzuka, dodany: 15.06.2010 00:02, tagi: zend_framework, php

Kaskadowość większości ludzi związanymi z web developementem pewnie kojarzy się z kaskadowymi arkuszami styli (CSS). Jest to poniekąd słuszne skojarzenie, ponieważ daje pewne wyobrażenie czym ona jest.

W CSS-ach kaskadowość znaczy nie mniej, nie więcej tyle, że właściwości stylu zdefiniowane dla elementów wyżej w drzewie dokumentu propagują się w dół drzewa aż do momentu kiedy nie zostaną nadpisane przez właściwości elementów niżej w hierarchii.

W PHP istnieje technika oparta na podobnych założeniach, realizowana dzięki magicznej funkcji __autoload. Na czym więc polega ? Załóżmy, że mamy pewną hierarchię klas: --A

---B ----E

---C ----D ----E Każda z tych klas dziedziczy bezpośrednio lub pośrednio z klasy A. Załóżmy teraz, że chcielibyśmy zmienić zachowanie wszystkich klas dziedziczących z A. Jednym z rozwiązań jest po prostu modyfikacja odpowiedniej metody z klasy A, jest to oczywiście najlepsze wyjście, z kilkoma wyjątkami, z których najpoważniejszym jest to, że klasa A należy do pewnej zewnętrznej biblioteki.

Klasycznym przykładem tego typu są formularze dziedziczące z Zend_Form. Mamy już gotowych n-bardzo dużo formularzy, jednak nagle okazało się, że trzeba im globalnie coś zmienić. Nie możemy za bardzo modyfikować samego Zend_Form-a ponieważ należy on do zewnętrznej biblioteki i w razie modyfikacji go, będzie problem z migracją do nowszych wersji frameworka.

Zmiana tych n-bardzo wielu klas, aby dziedziczyły z innej pośredniej klasy, która dziedziczy z Zend_Form będzie procesem bardzo pracochłonnym.

W tym momencie z pomocą przychodzi nam kaskadowość. Dzięki niej będziemy mogli podmienić oryginalną klasę Zend_Form, bez modyfikacji jej kodu.

Po pierwsze będzie nam potrzebna odpowiednia struktura katalogów: -test_cascading --base ---application ----forms ---library ----Zend --modified ---library ----Zend

Naszą zmodyfikowaną klasę Zend_Form umieścimy w podkatalogu “modified” w takiej samej ścieżce jak w katalogu “base”.

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

Autor wpisu: batman, dodany: 13.06.2010 14:00, tagi: zend_framework

Razem z Zend Framework otrzymujemy kilka ciekawych helperów akcji. Jeden z nich opisałem we wpisie zatytułowanym Zend Framework i AjaxContext. Innym ciekawym helperem jest FlashMessenger.

Jego działanie polega na jednokrotnym wyświetleniu użytkownikowi wiadomości o pewnym zdarzeniu, np pomyślnym zapisaniu danych w bazie. Po odświeżeniu strony z wiadomością, nie zostanie ona wyświetlona ponownie (chyba że znów będzie miało miejsce odpowiednie zdarzenie).

Korzystanie z FlashMessengera jest bardzo proste i sprowadza się do kilku linii kodu.

class JakisController extends Zend_Controller_Action
{
	protected $_flashMessenger = null;

	public function init()
	{
		// inicjalizacja helpera
		$this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
	}

	public function indexAction()
	{
		// pobranie wiadomości błyskawicznych i przekazanie ich do widoku
		$this->view->messages = $this->_flashMessenger->getMessages();
	}

	public function editAction()
	{
		// dodanie wiadomości
		$this->_flashMessenger->addMessage('Treść wiadomości błyskawicznej');
	}
}

Samo wyświetlenie wiadomości w widoku jest banalnie proste

<?php if(count($this->messages) > 0): ?>
	<ul id="messages">
		<?php foreach($this->messages as $message): ?>
			<li><?php echo $message; ?>
		<?php endforeach; ?>
	</li></ul>
<?php endif; ?>

Powyższy kod to tylko przykład. Najlepszym rozwiązaniem będzie zamieszczenie go w helperze widoku.

W niektórych przypadkach obraz wart jest więcej niż tysiąc słów. Tak jest też w tym przypadku. Demo wiadomości błyskawicznych możecie zobaczyć pod adresem http://demo.wilgucki.pl/flashmessenger

Autor wpisu: batman, dodany: 10.06.2010 11:28, tagi: zend_framework

W jednym z poprzednich wpisów poświęconych Zend_Form opisałem sposób dekorowania formularzy w Zend Framework. Niestety poważną wadą tej metody było powtarzanie kodu, co w przypadku rozbudowanej aplikacji nie jest najlepszym pomysłem. Dobrym wyjściem z tej sytuacji będzie zastosowanie klasy bazowej dla naszych formularzy, dziedziczącej po Zend_Form. Klasa ta będzie odpowiedzialna za ustawienie dekoratorów oraz wyrenderowanie formularza.

Przykładowy formularz wygląda następująco

class Application_Form_Example extends Batman_Form
{
	protected function _renderForm()
	{
		$this->setName('form-example');

		$name = new Zend_Form_Element_Text('name');
		$name->setLabel('Nazwa');
		
		$submit = new Zend_Form_Element_Submit('btn_save');
		$submit->setLabel('Zapisz');
		
		$this->addElement($name);
		$this->addElement($submit);
	}
}

Jak widać jedyną różnicą w stosunku do standardowego podejścia jest zmiana nazwy metody, w której znajduje się kod odpowiedzialny za formularz. Ponadto metoda ta nie zawiera kodu odpowiedzialnego za dekoratory.

Cała magia dzieje się w klasie Batman_Form

abstract class Batman_Form extends Zend_Form
{
	abstract protected function _renderForm();
	
	public function init()
	{
		$this->_renderForm();
		$this->_resetDecorators();
	}
	
	protected function _resetDecorators()
	{
		$this->clearDecorators();
		$this->addDecorator('FormElements')
			 ->addDecorator('HtmlTag', array('tag' => 'div'))
			 ->addDecorator('Form');

		$this->setElementDecorators(array(
			array('ViewHelper'),
			array('Errors'),
			array('Label'),
			array('HtmlTag', array('tag' => 'div', 'class' => 'element-group'))
		));
        
		// dodatkowe operacje na konkretnych elementach formularza
		// np zmiana nazwy klasy css dla przycisków
		foreach($this->getElements() as $element) {
			if($element instanceof Zend_Form_Element_Submit) {
				$element->removeDecorator('Label');
				$element->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'submit-group'));
			}
		}
	}
}

Klasa przesłania metodę init, która jest wywoływana z konstruktora Zend_Form. W metodzie tej wywoływana jest z kolei metoda klasy potomka o nazwie _renderForm oraz metoda _resetDecorators. Konstrukcja ta posiada dwie zalety. Po pierwsze klasa formularza odpowiada jedynie za utworzenie elementów. Po drugie nie musimy powielać kodu odpowiedzialnego za dekoratory. Dla zachowania porządku oraz dla zapewnienia poprawności kodu, klasa Batman_Form jest klasą abstrakcyjną, zawierającą abstrakcyjną metodę _renderForm.

A co jeśli w jednym formularzu mają być użyte inne dekoratory? Nic prostszego. Wystarczy w klasie formularza przesłonić metodę _resetDecorators i napisać własny kod odpowiedzialny za dekoratory.

Autor wpisu: sokzzuka, dodany: 09.06.2010 21:29, tagi: php

Witam w drugiej części tutoriala. Tematem na dziś będzie łączenie się z bazą MySql i wykonywanie prostych zapytań typu insert czy select. Pierwszą rzeczą jaką trzeba wykonać by móc podłączyć się do bazy MySql jest ściągnięcie sterownika. Paczki dla *nixów są na stronie projektu.Dostępne są też instalki dla Windowsa.

Po zainstalowaniu drivera tworzymy dwa pliki. Pierwszy będzie się nazywał common_db, będziemy go includować za każdym razem gdy będziemy coś robić z bazą, w pliku tym jest funkcja connect, która utworzy nam połączenie z odpowiednimi parametrami:

import MySQLdb
 
def connect():
    oDb = MySQLdb.connect (host="localhost", user="root", db="test")
    return oDb

Dla porównania plik php-owy, który robi dokładnie to samo:

$oDb = new PDO('mysql:dbname=test;host=127.0.0.1', 'root');

Natomiast drugi plik nazywa się test_insert.py , w którym mamy insertowanie danych do bazy:

import common_db
 
oDb = common_db.connect()
content = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
 
def test(path):
    global oDb
    global content
 
    oDb.begin()
    cursor = oDb.cursor()
 
    result = ''
    for i in range(0,1000):
        cursor.execute ("INSERT INTO test1(content) values ('"+content+"')")
        result += "inserted row number"+str(i)+"<br/>"
 
    oDb.commit()
    cursor.close()
    oDb.close ()
 
    return result

Ekwiwalent php:

require 'lib/common_db.php';
 
$sContent = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ';
 
set_time_limit(0);
 
for ($i=0; $i < 1000; $i++){
    $sQuery = "INSERT INTO test1(content) values ('$sContent')";
    $oDb->exec($sQuery);
    echo "inserted row number $i <br/>";
}

Są to skrypty do testów obciążeniowych więc nie dziwcie się, że zapytania tam idą w pętli, oczywiście globale też są złe ;) . Driver Pythonowy nie ma defaultowo ustawionego auto-commita więc wszystkie zapytania typu insert, update, delete trzeba przeprowadzać w transakcji.

Pobieranie z bazy – test_select.py:

import common_db
import random
 
oDb = common_db.connect()
 
def getRandomId(table):
    global oDb
    cursor = oDb.cursor()
    sQuery = 'select max(id) as max, min(id) as min from '+table;
    cursor.execute(sQuery)
    iMax, iMin = cursor.fetchone()
 
    return random.randint(int(iMin),int(iMax))
 
def test(path):
    global oDb
    cursor = oDb.cursor()
    cursor.execute('select * from test1 where id = %s',getRandomId('test1'))
    return cursor.fetchall()

Ekwiwalent php-owy

require 'lib/common_db.php';
function getRandomId($table) {
    global $oDb;
    $sCountStatement = 'select max(id) as max, min(id) as min from '.$table;
    $oCountStatement = $oDb->query($sCountStatement);
    list( $iMin,$iMax ) = $oCountStatement->fetch(PDO::FETCH_NUM);
 
    return rand($iMin,$iMax);
}
 
$sStatement = 'select * from test1 where id = ?';
$oQuery = $oDb->prepare( $sStatement );
 
$iId = getRandomId('test1');
 
$oQuery->execute(array($iId));
$aResult = $oQuery->fetch(PDO::FETCH_ASSOC);
 
echo($aResult['content']);

Aby przetestować powyższy kod, należy utworzyć w katalogu z projektem z pierwszej części tutorialu ww. pliki, uruchomienie jak poprzednio – http://localhost/py/nazwa_pliku_z_przykladem.py.

Jeszcze na koniec taka mała dygresja, w pierwszej części opisałem jak obsługiwać błędy, niestety informacje, które dostawaliśmy z wyjątku były dość skąpe i rzadko pozwalały zidentyfikować problem.

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.