This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!
Framework symfony jest na tyle złożonym tworem programistycznym, że czasami możemy po prostu “odbić się od ściany” próbując osiągnąć teoretycznie prostą rzecz. Moim zdaniem to dobrze, że wymaga on tak dużo od programisty - dzięki temu programista musi rozumieć, dlaczego i jak działają tworzone przez niego rozwiązania. W dzisiejszym wpisie chciałbym podjąć dosyć zaawansowaną kwestię związaną z modułem routingu - automatyczną zamianę adresu front-controllera backend.php [lub innego, nie będącego domyślnym] na “ładny” prefiks - np. /admin.
Fotografia: AgencePix, CC-BY-SA.
symfony: Zamiana adresu front-controllera backend.php na /admin.
Sztuka ta zostanie odegrana w trzech aktach:- Dodanie nowego wpisu do pliku .htaccess,
- Dodanie filtra routingu w pliku factories.yml,
- Dodanie klasy filtra w aplikacji backend.
Options +FollowSymLinks +ExecCGI
<IfModule mod_rewrite.c>
RewriteEngine On
# uncomment the following line, if you are having trouble
# getting no_script_name to work
RewriteBase /
# we skip all files with .something
#RewriteCond %{REQUEST_URI} \..+$
#RewriteCond %{REQUEST_URI} !\.html$
#RewriteRule .* - [L]
# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
# no, so we redirect to our front web controller
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
i dodajemy do niego następujące linijki:
RewriteCond %{REQUEST_URI} ^/admin/?
RewriteRule ^(.*)$ backend.php [QSA,L]
Całość wygląda następująco:
[code highlight=“19,20”] Options +FollowSymLinks +ExecCGI
uncomment the following line, if you are having trouble
getting no_script_name to work
RewriteBase /
we skip all files with .something
#RewriteCond %{REQUEST_URI} ..+$ #RewriteCond %{REQUEST_URI} !.html$ #RewriteRule .* - [L]
we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA] RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_URI} ^/admin/? RewriteRule ^(.*)$ backend.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
no, so we redirect to our front web controller
RewriteRule ^(.*)$ index.php [QSA,L]
<strong>Spowoduje to, że wszystkie żądania rozpoczynające się od frazy "admin" będą przekazywane nie do domyślnego front-controllera index.php, ale do wybranej przez nas aplikacji.</strong> Nie musi być to backend.php - możemy w ten sposób podstawić dowolną aplikację zawartą w naszym projekcie.
Teraz czas na plik factories.yml, który wygląda nastepująco [apps/backend/config/factories.yml]:
You can find more information about this file on the symfony website:
http://www.symfony-project.org/reference/1_4/en/05-Factories
prod: logger: class: sfNoLogger param: level: err loggers: ~
test: storage: class: sfSessionTestStorage param: session_path: %SF_TEST_CACHE_DIR%/sessions
response: class: sfWebResponse param: send_http_headers: false
mailer: param: delivery_strategy: none
dev: mailer: param: delivery_strategy: none
all: view_cache_manager: class: sfViewCacheManager param: cache_key_use_vary_headers: true cache_key_use_host_name: true
Dodajemy do niego filtr routingu [w tym przypadku jego nazwa to RoutingFilter] wpisując następujące linijki:
routing: class: RoutingFilter param: prefix: /admin generate_shortest_url: true extra_parameters_as_query_string: true
Po edycji całość prezentuje się tak:
[code highlight="32,33,34,35,36,37"]
# You can find more information about this file on the symfony website:
# http://www.symfony-project.org/reference/1_4/en/05-Factories
prod:
logger:
class: sfNoLogger
param:
level: err
loggers: ~
test:
storage:
class: sfSessionTestStorage
param:
session_path: %SF_TEST_CACHE_DIR%/sessions
response:
class: sfWebResponse
param:
send_http_headers: false
mailer:
param:
delivery_strategy: none
dev:
mailer:
param:
delivery_strategy: none
all:
routing:
class: RoutingFilter
param:
prefix: /admin
generate_shortest_url: true
extra_parameters_as_query_string: true
view_cache_manager:
class: sfViewCacheManager
param:
cache_key_use_vary_headers: true
cache_key_use_host_name: true
Ta zmiana spowoduje, że na etapie przetwarzania routingu i wyboru właściwej ścieżki zostanie wykorzystana nasza klasa RoutingFilter, która “pomoże” nieco frameworkowi skierować go na dobrą ścieżkę. Zobaczmy więc, jak wygląda kod tej klasy [/apps/backend/lib/RoutingFilter.class.php]:
|
|
Jak widzimy klasa RoutingFilter dziedziczy po sfPatternRouting, co znaczy, że może być filtrem dla routingu symfony. Jeśli przeczytaliście dokładnie zmiany wprowadzone do pliku factories.yml, na pewno zauważyliście, że jedna z linijek zawierała informację:
prefix: /admin
Ten prefiks właśnie określa początek części parametrów URLa przychodzącego żądania. W metodzie initialize() dane dotyczące tego prefiksu są pobierane z pliku factories.yml [a dokładniej odczytywane z tablicy parametrów - $options], a następnie w metodzie getRouteThatMatchesUrl() [nie ma to, jak opisowe nazwy metod, co nie? ;]] sprawdzamy, czy dany prefiks występuje w adresie. Jeśli występuje, jest wycinany po to, aby mógł zostać poprawnie dopasowany do danych w pliku routing.yml [którego ścieżki nie zawierają prefiksu /admin], jeśli nie, jest zostawiany bez zmian. oprócz tego znajduje się oczywiście przypadek, kiedy URL jest pusty - kierujemy wtedy framework w stron routingu obsługującego stronę główną aplikacji.
Jak sami widzicie to, co zrobiłem nie było wcale takie trudne. Jednakże sam nie wiedziałbym o tym, gdybym w pewnym momencie nie trafił na odpowiednie informacje gdzie indziej. To jest z jednej strony plus, a z drugiej strony zmora programistów wykorzystujących framework symfony, że o wielu rzeczach po prostu trzeba wiedzieć, bo domyślić się zwyczajnie nie da. Cóż, zobaczymy, jak przy “jedynce” wypadnie stabilna wersja Symfony2, która może za parę miesięcy ujrzy światło dzienne. ;]