środa, 16 września 2009

nfs - dlaczego nie działa?

Niby wszystko ładnie pięknie a tu ... nie wstaje po restarcie maszyny.
Tak samo jak apache z wirtualną domeną podpiętą pod zasób udostępniany przez nfs.
Chwila googlowania i jest powód oraz rozwiązanie sytuacji.
Zasób montowany poprzez fstab nie może zostać przypięty ponieważ podczas przetwarzania fstab-a nie działa jeszcze sieć. Z pomocą przychodzi pakiet autofs.
Do pliku /etc/auto.master dodałem

/home/cos_tam /etc/auto.nfs --timeout=3600 --ghost

oraz stworzyłem plik /etc/auto.nfs w którym podaje opcje do montowania zasobu:

katalog -fstype=nfs,soft,intr,rsize=8192,wsize=8192 adres_ip:/home/katalog_zdalny


... i tak na pozostałych maszynach.

czwartek, 1 stycznia 2009

Eh dawno mnie tu nie było

No to trzeba nadrabiać zaległości. Zacznijmy od początku... a może to właściwie koniec. Wszystko jedno sylwester. A ten był spędzony we Wrocławiu ze zgrają mafijnych koleszków.



To zdjęcie najlepiej oddaje charakter zabawy, wielkie dzięki wszystkim uczestnikom.



To zresztą też :)



A to radosna twórczość Mateusza - super

czwartek, 11 września 2008

Symfony krok 5

W pliku /apps/frontend/config/routing.yml można ustawić która strona zostaje wyświetlona jako "index" naszej aplikacji:


homepage:
url: /
param: { module: question, action: list }

wtorek, 9 września 2008

Symfony user managmet and sessions

Sesje użytkowników są przechowywane w symfony w obiekcie User. Możemy pobrać ten obiekt w dowolnym miejscu za pomocą metody getUser();


class mymoduleActions extends sfActions
{
public function executeFirstPage()
{
$nickname = $this->getRequestParameter('nickname');

// Store data in the user session
$this->getUser()->setAttribute('nickname', $nickname);
}

public function executeSecondPage()
{
// Retrieve data from the user session with a default value
$nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward');
}
}


Oczywiście w tym obiekcie możemy przechowywać obiekty (niezalecane) i zmienne które będą dostępne pomiędzy wywołaniami stron.

Zmienne możemy ustawiać, pobierać i czyścić


class mymoduleActions extends sfActions
{
public function executeRemoveNickname()
{
$this->getUser()->getAttributeHolder()->remove('nickname');
}

public function executeCleanup()
{
$this->getUser()->getAttributeHolder()->clear();
}
}


Do sesji użytkownika możemy dobrać się także z szablonów



Hello, getAttribute('nickname') ?>




Jeśli potrzebujemy przechować informację tylko do kolejnego wywołania, np potwierdzenie zapisania/usunięcia/edycji danych możemy skorzystać z metody setFlash i getFlash. Dane zapisane za pomocą tych metod zostaną usunięte przy kolejnym wywołaniu.
np.

$this->setFlash('attrib', $value);
$value = $this->getFlash('attrib');

// i w szablonach

has('attrib')): ?>
get('attrib') ?>



Ciasteczka

W symfony możemy decydować gdzie zapisane są ciasteczka. Określamy to w pliku apps/myapp/config/factories.yml


all:
storage:
class: sfSessionStorage
param:
session_name: my_cookie_name


Po stronie aplikacji sesje są przechowywane domyślnie w plikach. Możemy to zmienić w pliku apps/myapp/config/factories.yml


all:
storage:
class: sfMySQLSessionStorage
param:
db_table: SESSION_TABLE_NAME # Name of the table storing the sessions
database: DATABASE_CONNECTION # Name of the database connection to use


Czas przechowywania sesji można ustawić w pliku apps/myapp/config/settings.yml


default:
.settings:
timeout: 1800 # Session lifetime in seconds

Symfony krok 4

W niektórych przypadkach potrzebujemy tylko wysłać nagłówek:


public function executeRefresh()
{
$output = '<"title","My basic letter"],["name","Mr Brown">';
$this->getResponse()->setHttpHeader("X-JSON", '('.$output.')');

return sfView::HEADER_ONLY;
}


lub wybrać szablon o dowolnej nazwie


$this->setTemplate('myCustomTemplate');


Przydatnymi metodami są także forward, redirect i forward404 (nie można znaleźć strony. Dzięki nim możemy przejść do innej akcji w innym module lub załadować dowolną stronę.


$this->forward('otherModule', 'index');

$this->redirect('otherModule/index');
$this->redirect('http://www.google.com/');

/**
* szablon do strony 404 znajduje się w $sf_symfony_data_dir/modules/default/
* możemy także nadpisać akcje error404 w dowolnym module, oraz dołączyć do modułu
*/
forward404();
/**
* istnieje tez kilka funkcji pomocniczych które ułatwią nam pracę
*/
forwardIf();
forwardUnless();
forward404If();
forward404Unless();
redirectIf();
redirectUnless();

$this->forward404If(!$article);


Jeśli mamy czynność którą musimy powtórzyć przed / po wykonaniu każdej akcji możemy utworzyć metody preExecute() i postExecute()

public function preExecute()
{
// The code inserted here is executed at the beginning of each action call
...
}

public function executeIndex()
{
...
}

public function executeList()
{
...
$this->myCustomMethod(); // Methods of the action class are accessible
}

public function postExecute()
{
// The code inserted here is executed at the end of each action call
...
}


Funkcje pomocne przy wykonywaniu akcji:


Name Function Sample Output
Request Information
getMethod() Request method Returns sfRequest::GET or sfRequest::POST constants
getMethodName() Request method name 'POST'
getHttpHeader('Server') Value of a given HTTP header 'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'
getCookie('foo') Value of a named cookie 'bar'
isXmlHttpRequest()* Is it an Ajax request? true
isSecure() Is it an SSL request? true
Request Parameters
hasParameter('foo') Is a parameter present in the request? true
getParameter('foo') Value of a named parameter 'bar'
getParameterHolder()->getAll() Array of all request parameters
URI-Related Information
getUri() Full URI 'http://localhost/myapp_dev.php/mymodule/myaction'
getPathInfo() Path info '/mymodule/myaction'
getReferer()** Referrer 'http://localhost/myapp_dev.php/'
getHost() Host name 'localhost'
getScriptName() Front controller path and name 'myapp_dev.php'
Client Browser Information
getLanguages() Array of accepted languages Array( [0] => fr [1] => fr_FR [2] => en_US [3] => en )
getCharsets() Array of accepted charsets Array( [0] => ISO-8859-1 [1] => UTF-8 [2] => * )
getAcceptableContentTypes() Array of accepted content types Array( [0] => text/xml [1] => text/html



Przykłady wykorzystania:

pobieranie parametrów:

class mymoduleActions extends sfActions
{
public function executeIndex()
{
$hasFoo = $this->getRequest()->hasParameter('foo');
$hasFoo = $this->hasRequestParameter('foo'); // Shorter version
$foo = $this->getRequest()->getParameter('foo');
$foo = $this->getRequestParameter('foo'); // Shorter version
}
}


upload plików:


class mymoduleActions extends sfActions
{
public function executeUpload()
{
if ($this->getRequest()->hasFiles())
{
foreach ($this->getRequest()->getFileNames() as $uploadedFile)
{
$fileName = $this->getRequest()->getFileName($uploadedFile);
$fileSize = $this->getRequest()->getFileSize($uploadedFile);
$fileType = $this->getRequest()->getFileType($uploadedFile);
$fileError = $this->getRequest()->hasFileError($uploadedFile);
$uploadDir = sfConfig::get('sf_upload_dir');
$this->getRequest()->moveFile($uploadedFile, $uploadDir.'/'.$fileName);
}
}
}
}

niedziela, 7 września 2008

Symfony krok 3

Uruchomienie kodu symfony z lini poleceń:



define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/..'));
define('SF_APP', 'myapp');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG', false);

require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');

// add code here


Front controller to coś co przyjmuje zapytania i interpretuje je. Można dodać nowy kontroler, ale mnie interesują tylko dwa kontrolery, które już są zdefiniowane. Mianowicie:


http://localhost/index.php/mymodule/index
http://localhost/mymodule/index

i

http://localhost/myapp_dev.php/mymodule/index


Nazewnictwo klas akcji

Nazwa klasy musi zaczynać się od nazwy modułu z małej litery i kończyć się na Actions. Klasa musi być umieszczona w katalogu pps/myapp/modules/mymodule/actions/ a plik musi nazywać się actions.class.php

Każda metoda przynosząca nowe akcje musi zaczynać się od execute, a cała klasa musi dziedziczyć po sfActions.


class mymoduleActions extends sfActions
{
public function executeIndex()
{

}
}


Nazwa wywołania url jest wzięta z nazwy funkcji w ten sposób że jeśli utworzymy funkcję executeList poprawnym url dla tej akcji jest

http://localhost/myapp_dev.php/mymodule/list
lub
http://localhost/mymodule/list

Możemy również tworzyć całe klasy dla poszczególnych akcji. Wtedy musimy dziedziczyć z klasy sfAction i tworzyć klasy o nazwie NazwaAkcjiAction.

np.
myapp/modules/mymodule/actions/indexAction.class.php


class indexAction extends sfAction
{
public function execute()
{
...
}
}


myapp/modules/mymodule/actions/listAction.class.php


class listAction extends sfAction
{
public function execute()
{
...
}
}


Pobieranie informacji o wywołaniu:


class mymoduleActions extends sfActions
{
public function executeIndex()
{
// Retrieving request parameters
$password = $this->getRequestParameter('password');

// Retrieving controller information
$moduleName = $this->getModuleName();
$actionName = $this->getActionName();

// Retrieving framework core objects
$request = $this->getRequest();
$userSession = $this->getUser();
$response = $this->getResponse();
$controller = $this->getController();
$context = $this->getContext();

// Setting action variables to pass information to the template
$this->setVar('foo', 'bar');
$this->foo = 'bar'; // Shorter version

}
}


Templates

Jeśli akcję zakończymy return sfView::SUCCESS;, lub jeśli nie podamy return do wywołania zostanie dołączony szablon actionNameSuccess.php. Możemy także zwrócić sfView::ERROR wtedy zostanie dołączony szablon actionNameError.php, ogólnie symfony szuka w szablonach pliku zwróconego prze return 'MyResult' + prefix nazwa akcji.

Można też:


return sfView::NONE;


i


$this->getResponse()->setContent("Hello, World!");

return sfView::NONE;


lub


return $this->renderText("Hello, World!");

sobota, 6 września 2008

Symfony krok 2

Definicja bazy danych jest przechowywana w pliku config/schema.yml lub config/schema.xml.
Przykładowy plik xml może wyglądać następująco:


<?xml version="1.0" encoding="UTF-8"?>
<database name="propel" defaultIdMethod="native" noxsd="true">
<table name="ask_question" phpName="Question">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="title" type="longvarchar" />
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
<column name="updated_at" type="timestamp" />
</table>

<table name="ask_answer" phpName="Answer">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="question_id" type="integer" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="body" type="longvarchar" />
<column name="created_at" type="timestamp" />
</table>

<table name="ask_user" phpName="User">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="nickname" type="varchar" size="50" />
<column name="first_name" type="varchar" size="100" />
<column name="last_name" type="varchar" size="100" />
<column name="created_at" type="timestamp" />
</table>

<table name="ask_interest" phpName="Interest">
<column name="question_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_question">
<reference local="question_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="created_at" type="timestamp" />
</table>

<table name="ask_relevancy" phpName="Relevancy">
<column name="answer_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_answer">
<reference local="answer_id" foreign="id"/>
</foreign-key>
<column name="user_id" type="integer" primaryKey="true" />
<foreign-key foreignTable="ask_user">
<reference local="user_id" foreign="id"/>
</foreign-key>
<column name="score" type="integer" />
<column name="created_at" type="timestamp" />
</table>

</database>


Jeśli chcemy wygenerować plik konfiguracyjny z istniejącej bazy danych wystarczy w pliku config/propel.ini
wyedytować linie:

propel.database.createUrl = mysql://marian@localhost/
propel.database.url = mysql://marian@localhost/askeet

i wydać komendę:

$ symfony propel-build-schema

Dla mnie najprzyjemniejszym narzędziem go graficznego tworzenia baz danych jest wtyczka Clay do Eclipse.
Aby wygenerować klasy reprezentujące tabele w bazie danych (na podstawie pliku xml lub yml) wystarczy wydać polecenie:

$ symfony propel-build-model

Aby symfony mogło połączyć się do bazy danych trzeba wyedytować plik config/database.yml (nie mylić z konfiguracją pluginu propel):

all:
propel:
class: sfPropelDatabase
param:
dsn: mysql://marian@localhost/askeet

! Ważna uwaga: w plikach yml trzeba używać spacji zamiast tabulatorów.

Jeśli nie mamy gotowej bazy danych możemy wygenerować skrytp sql z konfiguracji schema.xml lub schema.yml:

$ symfony propel-build-sql
$ mysql -u youruser -p askeet <> lub
$ symfony propel-insert-sql

Kolejnym krokiem jest wygenerowanie formularzy do operacji CRUD (miło szybko zobaczyć coś działającego):

// ciekawe że trzeba wykonać ten krok w symfony 1.1 (w askeet tutorial ten krok jest pominięty gdyż tutorial traktuje o symfony 1.0)
$ symfony propel:build-forms

// no i właściwy proces generowania
$ symfony propel-generate-crud frontend question Question