CakePHP: Przyjazne linki

Tak jak obiecywałem, postanowiłem napisać małą serię artykułów o CakePHP, w wersji 1.3, chociaż znaczna większość ma identyczne zastosowanie w wersji 1.2. Zacznę od wyjaśnienia się co to są przyjazne linki, znane szerzej jako Search Engine Friendly URLs.

Przyjazne linkowanie służy głównie do lepszego indeksowania naszej strony przez wyszukiwarki internetowe, np. Google. Efekt uboczny to taki, że linki lepiej oddają swoją treść. Najlepiej pokazać to na przykładzie.
Tutaj mała dygresja, w każdym z artykułów zakładam, że Czytelnik jest zapoznany z podstawami CakePHP, jeżeli tak nie jest to odsyłam do najlepszego źródła: CakePHP Cookbook. Dodatkowo, trzymam się konwencji angielskiego nazewnictwa.
Załóżmy, że mamy kontroler Posts, i model Post, który jest oczywiście postem, bądź w moim przypadku artykułem. Nie pytajcie dlaczego nie nazwałem tego Article. 😉 Chcąc stworzyć link do konkretnego artykułu, w kontrolerze tworzymy metodę view. Zapełniamy ją dość oczywistym kodem:

$this->Post->id = $id;
$post = $this->Post->find('first');
$this->set('post', $post);

Stosujemy find, zamiast read, gdyż nie ma potrzeby zapełniać kontroler danymi. Teraz możemy utworzyć widok naszego artykułu, dla naszego przykładu wystarczy najprostszy z możliwych:

<?php echo $post['Post']['body']; ?>

Teraz możemy się odwołać do Naszego artykułu poprzez link: http://localhost/posts/view/1. Oczywiście jest to link, który kompletnie nic nie mówi użytkownikowi o treści jakiej prezentuje, oraz nie jest przyjazny dla wyszukiwarek. W moim przypadku chciałem osiągnąć następującą postać linku: http://localhost/artykul/id_artykulu-tytul_artykulu. Aby to osiągnąć musimy skorzystać z router’a CakePHP.

Otwieramy plik /app/config/routes.php. W tym pliku będzie się dziać cała magia. Robiąc to krok po kroku, najpierw chcemy uzyskać postać http://localhost/artykul/id. Osiągamy to poprzez dodanie następującego kodu:

Router::connect(
        '/artykul/:id',
        array(
            'controller' => 'posts',
            'action' => 'view'),
        array(
            'pass' => array('id'),
            'id' => '[0-9]+'
        )
);

Zakładamy tutaj, że pole id przyjmuje wartości numeryczne. Chcąc uzyskać postać końcową zamieniamy ten fragment kodu na następujący:

Router::connect(
        '/artykul/:id-:slug',
        array(
            'controller' => 'posts',
            'action' => 'view'),
        array(
            'pass' => array('id', 'slug'),
            'id' => '[0-9]+',
            'slug'=>'.+'
        )
);

Teraz pojawia się pytanie, jak z poziomu CakePHP uzyskać następujące linki. W widoku w którym chcemy wyświetlić link piszemy:

<?php echo $html->link('Link',
   array(
      'controller' => 'posts',
      'action' => 'view',
      'id'=>$id,
      'slug'=>$slug
   )
); ?>

Część z Was może się spytać co to jest to tajemnicze $slug. Jest to krótki ciąg znaków, najlepiej oddający treść linka. Nie może zawierać spacji, ani znaków specjalnych; zamieniamy je najlepiej na myślnik. Dodatkowo slug jest zoptymalizownay pod względem SEO. W Naszym przypadku najlepszym kandydatem na slug‚a będzie tytuł artykułu. Musimy go jednak odpowiednio obrobić. Na szczęście CakePHP posiada do tego odpowiednie narzędzie.
Posługujemy się metodą Inflector::slug('tytuł pierwszego artykułu','-');. Pierwszy argument to ciąg znaków do ‚oczyszczenia’, a kolejny to znak, na jaki zamieniamy znaki niedozwolone. Wynikiem będzie tytuł-pierwszego-artykułu. Nie jest to do końca poprawne, nie możemy posługiwać się znakami specjalnymi w adresie, jest to niezgodne z formatem URI. Niestety na dzień dzisiejszy CakePHP w wersji 1.3 nie zamienia znaków diaktrycznych na ich łacińskie odpowiedniki. Aby dodać taką funkcję, możemy stworzyć własną funkcję do tego celu, bądź też dokonać zmiany w pliku /cake/libs/inflector.php. Zmiany dokonujemy w metodzie slug, zamieniając wartość zmiennej $default na:

$default = array(
        '/?|á|?|â|ą/' => 'a',
        '/?|é|?|?|ë|ę/' => 'e',
        '/?|í|î/' => 'i',
        '/?|ó|ô|?|ó/' => 'o',
        '/?|ú|ů|?/' => 'u',
        '/ç|ć/' => 'c',
        '/ł/'=>'l',
        '/ś/'=>'s',
        '/?|ń/' => 'n',
        '/ż|ź/'=>'z',
        '/ä|?/' => 'ae',
        '/ö/' => 'oe',
        '/ü/' => 'ue',
        '/Ä/' => 'Ae',
        '/Ü/' => 'Ue',
        '/Ö/' => 'Oe',
        '/ß/' => 'ss',
        '/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
        '/\\s+/' => $replacement,
        sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
);

Spowoduje to poprawne tworzenie slugów z polskimi znakami. Musimy jednak pamiętać o dokonywanie takiej modyfikacji przy każdej aktualizacji CakePHP. Liczę na to, że w przyszłych wersjach ta funkcjonalność będzie w oficjalnych źródłach. Odpowiedni ticket został już złożony. 😉 Pozdrawiam i mam nadzieję, że się przyda.