Serwisowanie aplikacji CakePHP

Zakupiłeś lub zamówiłeś wykonanie własnego projektu, aplikacji...

outsourcing programistyczny

Outsourcing CakePHP polega na przejmowaniu części lub całości zlecenia Klienta...

Hosting dedykowany dla aplikacji w CakePHP

Hostingi dostępne na naszym rynku nie spełniają Twoich oczekiwań? Nakładają...

2013-10-01 ⇔ 2019-01-18

  • 100%
    POMYSŁ
  • 100.00%
    THE END

Na pierwszy rzut oka w CakePHP trudno pojąć dlaczego mamy używać do linków jakiegoś „$this->Html->link()” skoro wszędzie wystarczy wpisać a href i po sprawie... - wybaczcie ale czasem trudno wymyślić dobry wstęp :) - idźmy do konkretu:

A href  w CakePHP się nie sprawdzi, CakePHP ma własną strukturę plików widoku oraz katalog webroot który dla załączonych zasobów jest punktem odniesienia, całość ogarniana jest przez przekierowania w .htaccess i konia z rzędu temu komu uda się w jednym widoku zrobić taki link "a href" który go nie-zawiedzie w pozostałych widokach generowanych w CakePHP...

Właściwie i jedynie słuszne rozwiązanie to linkowanie narzędziami HtmlHelpera ->link() i ->url(), tym pierwszym zajmiemy się w tym artykule, ->url() poruszymy jedynie, i opiszemy sobie jeszcze ->image() oraz kombinację ->link() i ->image(), poniekąd to bardzo proste rozwiązania, ale dla wielu początkujących kejkowców to nightmare – strach z wielkimi oczami :)

Zaczynamy:

CakePHP $this->Html->link() konstruujemy w ten sposób

<?php
$this->Html->link('jakiś tekst linku', 'adres linku jako string lub tablica', array(
    //tablica atrybutów:
    'class' => 'klasa',
    'style' => 'styl'
        //itp.
));
?> 

„adres linku jako string lub tablica” - no tu musimy się troszkę rozdrobnić bo to oznacza że adres możemy zapisać na wiele sposobów w zależności od potrzeb, ponieważ ->link() jest ważnym punktem na drodze przyszłych programistów CakePHP chciałbym poświęcić tej części więcej uwagi, gwoli wyjaśnienia:

CakePHP jest „automagiczny” niekiedy tak bardzo że osoby przyzwyczajone do pełnej kontroli nad swoimi aplikacjami nie potrafią przestawić swojego rozumowania na „nie zawracam sobie tym głowy, samo się dokona”, wiem bo długo sam miałem z tym kłopot, a w sprawie linków automagia działa równie zaskakująco :) jak w przypadku formularzy, ale nawet tu magia działa wg określonych zasad i z samą magią ma tyle wspólnego co ja z księżycem...

W linkach CakePHP mamy 2 kluczowe elementy o których ZAWSZE pamiętamy:

  1. prawy ukośnik „/” (tzw. Slash)
  2. treść linku (tzw. slug) lub wartość liczbową

dlaczego to takie istotne? Przecież każdy wie że tak wygląda link: /jakis_link/jakas_cyfra, co w tym niezwykłego? Ano... sposób, metoda stosowania :) domyślnie CakePHP zakłada że link zbudowany jest na hierarchii:

  • /controller/actions/pass

w tym przypadku „pierwszy slash” to odniesienie do root i oznacza ze link jest zaczynany od domeny strony (root), następny człon to domyślnie nazwa kontrolera, potem akcji w tym kontrolerze, potem idą dane GET w tablicy parametru pass, danych tych może być wiele.

dla przykładu adresy:

„/sites/view/2/3/4/5/6”
co znaczy: użyj „SitesController.php” w nim akcji(metody) „view” przekazując przez params['pass'] dane w postaci tablicy array(0=>2, 1=>3, 2=>4, 3=>5, 4=>6)

„/sites/view/dupa”
co znaczy: użyj „SitesController.php” w nim akcji(metody) „view” przekazując „slug” przez params['pass'] dane w tablicy array(0=>'dupa')

ale UWAGA! tak zapisany link (bez pierwszego slash):

„sites/view/dupa”
znaczy: w tym kontrolerze z którego wyświetlamy widok z w/w linkiem przekaż akcji/metodzie „sites” parametry przez params['pass'] jako dane w tablicy array(0=>'view', 1=>'dupa') - no i lipa.

Jak widać brak lub istnienie pierwszego slash „/” zmienia diametralnie znaczenie całego linku, dlatego drodzy Kejkowi kadeci polecam sobie to zapamiętać, jako dość ważne :) no i automagia linków CakePHP was nie pozagina – hehe.

Dobra a co w takim razie z takim linkiem:

„/sites”
ano znaczy tyle że prosimy o controller Sites i tyle, ale ponieważ naturalnym jest że sam controler nic nam nie da, więc CakePHP domyślnie będzie poszukiwał akcji/metody index by obsłużyć to żądanie

„/sites/43” zrobi to samo co wyżej tyle że zamiast index będzie poszukiwał akcji/metody „43”, i ten link nam oczywiście nie wyjdzie, używając domyślnego routingu musielibyśmy go skonstruować tak „/sites/view/43” lub odnieść się do kontrolera z którego wychodzimy przez bezpośrednie „view/43” z pominięciem pierwszego slash.

Chociaż sam jestem zwolennikiem linków typu string, to linkowanie można opisywać tablicą, w zasadzie to precyzyjne rozwiązanie, ale oznacza więcej pisania :)

zamiast string możemy pisać tablicę

  • „/sites/view/dupa” piszemy array('controller'=>'sites', 'action'=>'view', 'dupa')
  • „/sites/view/2/3/4/5/6” piszemy array('controller'=>'sites', 'action'=>'view', '2','3','4','5','6')
  • „view/dupa” piszemy array('action'=>'view', 'dupa')
Dopóki nie będziemy tworzyli własnych reguł routingu obowiązują nas powyższe zasady pisania adresu linku, i żadne inne nam nie będą chciały działać w prawidłowy sposób!

tyle teorii przejdźmy do praktyki i przykładów.

 Zakładamy że mamy kontroler „ArticlesController.php” i w nim dwie akcje/metody „index” która wyświetla nam listę artykułów oraz „view” która wyświetla nam konkretny artykuł, oraz dwa widoki „View/Articles/index.ctp” i  „View/Articles/view.ctp”, całość pobiera dane z tabeli „articles” w bazie z użyciem modelu „Article.php” - skomplikowane? Hehe, NIE.

baza danych:

CREATE TABLE articles (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255),
    content TEXT,
);
/* wrzućmy od razu dane */
INSERT INTO articles (title,content)
    VALUES ('tytuł 1', 'jakaś treść artykułu 1');
INSERT INTO articles (title,content)
    VALUES ('tytuł 2', 'jakaś treść artykułu 2');

Model: Article.php (właściwie moglibyśmy go pominąć, bo istnieje zgodność nazw pomiędzy kontrolerem, modelem i tabelą w bazie)

<?php
// Model/Article.php
class Article extends AppModel {

    public $name = 'Article';
    
}

Kontroler ArticlesController.php, w tym przykładzie to max uproszczone zapytania, jedynie dla zobrazowania przykładu.

<?php
// Controller/ArticlesController.php
class ArticlesController extends AppController {

    // wskażemy kontrolerowi model Article.php który ma używać globalnie
    public $uses = array('Article');

    public function index() {
        $this->set('articles', $this->Article->find('list'));
        /* do View/Articles/index.ctp zwróci nam tablicę w postaci:
          $articles['id']['title'];

          Array
          (
            [1] => tytuł 1
            [2] => tytuł 2
          )
         */
    }

    public function view($id = null) {
        $this->set('article', $this->Article->findById($id));
        /* do View/Articles/view.ctp zwróci nam tablicę w postaci:
          $article['id'];
          $article['title'];
          $article['content'];

          Array
          (
              [Article] => Array
                  (
                      [id] => wybrany id
                      [title] => jakiś tam tytuł
                      [content] => Jakaś treść artykułu....
                  )
          )
         */
    }

}

Mamy działającą aplikację to stwórzmy sobie teraz nasze linki w widoku View/Articles/index.ctp

<?php
/*ponieważ nasza zmienna $articles to tablica z danymi musimy ją iterować
jeżeli chcesz zobaczyć co zawiera użyj wydruku:
pr($articles);
*/
foreach($articles as $key => $value){
    //otrzymujemy $key = id, $value = title
    
    //przykład 1: /controler/actions/pass
    echo $this->Html->link($value, '/articles/view/'.$key);
    
    //przykład 2: actions/pass
    echo $this->Html->link($value, 'view/'.$key);
    
    /* to samo na tablicach */
    
    //przykład 3: array('controller'=>'nasz_kontroler', 'action'=>'akcja', 'pass')
    echo $this->Html->link($value, array('controller'=>'articles', 'action'=>'view', $key));
    
    //przykład 4: array('action'=>'akcja', 'pass')
    echo $this->Html->link($value, array('action'=>'view', $key));
    
    /* wynik ten sam dla wszystkich: <a href="/articles/view/1">Tytuł 1</a>
     * w przypadku przykładu 2 i 4
     * CakePHP sam dopisze odpowiedni kontroller do linku :)
     */
}
?>

w /View/Articles/view.ctp wystarczy wpisać <?php pr($article); ?> żeby zobaczyć rezultat - miłych eksperymentów... to bułka z masłem.

szybko o CakePHP $this->Html->image()

HtmlHelper::image() zakłada domyślnie że wszystkie obrazki trzymasz w katalogu webroot/img, dlatego nie musisz pisać ścieżki do pliku, chyba że to zmienisz... Konstrukcja ->image():

<?php
$this->Html->image('obrazek.jpg', array(
    //tablica atrybutów:
    'alt' => 'treść altu'
    'class' => 'klasa',
    'style' => 'styl'
        //itp.
));
?> 

poniżej przykłady dla zasobów zlokalizowanych w folderach:

  • webroot/img/obrazek.jpg,
  • webroot/img/moj_katalog/obrazek.jpg,
  • webroot/inny_katalog/obrazek.jpg
<?php

    // webroot/img/obrazek.jpg
    $this->Html->image('obrazek.jpg');
    // wynik: <img src="/img/obrazek.jpg" />

    // webroot/img/moj_katalog/obrazek.jpg
    $this->Html->image('moj_katalog/obrazek.jpg');
    // wynik: <img src="/img/moj_katalog/obrazek.jpg" />

    // webroot/inny_katalog/obrazek.jpg
    $this->Html->image('/inny_katalog/obrazek.jpg');
    // wynik: <img src="/inny_katalog/obrazek.jpg" />

?> 

proste, i nie wymaga jakiegoś większego tłumaczenia, w przykładach łatwo wyłapać różnice i domyślić się jak należy zwracać się po dany zasób, jak i w przypadku ->link() pamiętajmy o roli „/” slasha (do analizy przykład 3).

Teraz CakePHP $this->Html->image() & $this->Html->link() razem

Wiemy jak mamy sobie skonstruować CakePHP-owy link i jak mamy sobie poradzić w prezentacją obrazka z HtmlHelper::image() - wbrew pozorom proste rozwiązanie jak osadzanie obrazków czy tagów w HtmlHelper::link(), wielu początkujących jednak tego nie rozumie...

zasada jest prosta i niczym wielkim się nie różni od konstrukcji linku, z wyjątkiem tego że zamiast nazwy linku wpisujemy nasz obrazek, co pozornie może wyglądać na zawiłe :)

<?php
echo $this->Html->link( 
        $this->Html->image('obrazek.jpg', array('parametry dla obrazka')) , 
        'link', 
        array('parametry dla linku') 
        );
?> 

 

Proste prawda? Należy jednak pamiętać aby w tablicy parametru linku umieścić 'escape' => false dzięki temu cake potraktuje obrazek jak zapis html, brak tego parametru jest częstą przyczyną dzięki której młodzi adepci zniechęcają się i wyszukują najróżniejsze rozwiązania by wyważyć otwarte drzwi.

Poniżej kilka przykładów jak poprawnie zapisać a href z obrazkiem:

<?php
echo $this->Html->link(
        $this->Html->image('obrazek.jpg', array(
        //atrybuty obrazka
            'alt' => 'treść altu', 
            'class' => 'klasa',
            'style' => 'styl'
        )),
        //adres linku
        '/articles/view/',
        //atrybuty linku
        array(
            'class' => 'klasa', 
            'style' => 'styl',
            'escape' => false
        )
    );

//prostszy zapis:

//obrazek jako zmienna
$obrazek = $this->Html->image('obrazek.jpg', array(
        //atrybuty obrazka
            'alt' => 'treść altu', 
            'class' => 'klasa',
            'style' => 'styl'
        ));

echo $this->Html->link(
        $obrazek,
        //adres linku
        '/articles/view/',
        //atrybuty linku
        array(
            'class' => 'klasa', 
            'style' => 'styl',
            'escape' => false
        )
    );
//lub w przypadku kiedy link nie ma atrybutów
echo $this->Html->link( $obrazek, '/articles/view/', array('escape' => false));
?> 

Miłej zabawy :)

(~BN) © cakephp.com.pl

przewiń do góry
Przetwarzanie... prosimy o cierpliwość.

Loading
(default) 12 queries took 2 ms
NrQueryErrorAffectedNum. rowsTook (ms)
1SELECT `Site`.`id`, `Site`.`type`, `Site`.`name`, `Site`.`slug`, `Site`.`hints`, `Site`.`title`, `Site`.`desc`, `Site`.`info` FROM `sql_cakephp`.`sites` AS `Site` WHERE `Site`.`slug` = 'cakephp-moje-przyklady' LIMIT 1110
2SELECT COUNT(*) AS `count` FROM `sql_cakephp`.`sites` AS `Site` WHERE `Site`.`id` IN (5, 2, 'moje Repo', 'cakephp-moje-przyklady', 6842, 'Przykłady uzycia kodu i rozwiązań w CakePHP 2+', 'Na tej stronie będę umieszczał kod po który muszę często sięgać i wklejać go w robione projekty, najczęściej poszukuję go w poprzednich pracach', '<p style=\"margin-bottom: 0cm;\">Na tej stronie będę umieszczał kod po kt&oacute;ry muszę często sięgać i wklejać go w robione projekty, najczęściej poszukuję go w poprzednich pracach co często mnie irytuje, zwłaszcza że to bardzo proste rozwiązania &ndash; ale być może skoncentrowanie ich w jednym miejscu i w formie kt&oacute;ra może przydać się początkującym CakePHP\'owcom &ndash; okaże się dla mnie doskonałym rozwiązaniem, będę starał się opisywać te kody tak aby przypadkowy poszukiwacz rozwiązań m&oacute;gł się jakoś w tym odnaleźć. Jakikolwiek podział na Widoki, Controllery czy Modele zrobię w miarę jak będzie przybywało rozwiązań i będę musiał je grupować.</p>')110
3SELECT `Article`.`id`, `Article`.`site_id`, `Article`.`showdate`, `Article`.`title`, `Article`.`active`, `Article`.`author`, `Article`.`slug`, `Article`.`hints`, `Article`.`content`, `Article`.`code`, `Article`.`idea`, `Article`.`database`, `Article`.`boulid`, `Article`.`backend`, `Article`.`frontend`, `Article`.`layout`, `Article`.`created`, `Article`.`modified`, `Site`.`id`, `Site`.`type`, `Site`.`name`, `Site`.`slug`, `Site`.`hints`, `Site`.`title`, `Site`.`desc`, `Site`.`info` FROM `sql_cakephp`.`articles` AS `Article` LEFT JOIN `sql_cakephp`.`sites` AS `Site` ON (`Article`.`site_id` = `Site`.`id`) WHERE `Article`.`slug` = 'CakePHP-this-Html-link-image-przyjazny-duet' AND `Article`.`site_id` = 5 AND `Article`.`active` = 1 ORDER BY `created` DESC111
4SELECT `Photo`.`id`, `ArticlesPhoto`.`photo_id`, `ArticlesPhoto`.`article_id` FROM `sql_cakephp`.`photos` AS `Photo` JOIN `sql_cakephp`.`articles_photos` AS `ArticlesPhoto` ON (`ArticlesPhoto`.`article_id` = 187 AND `ArticlesPhoto`.`photo_id` = `Photo`.`id`) 110
5SELECT `Addfile`.`id`, `Addfile`.`title`, `Addfile`.`name`, `Addfile`.`hints`, `Addfile`.`size`, `Addfile`.`type`, `AddfilesArticle`.`addfile_id`, `AddfilesArticle`.`article_id` FROM `sql_cakephp`.`addfiles` AS `Addfile` JOIN `sql_cakephp`.`addfiles_articles` AS `AddfilesArticle` ON (`AddfilesArticle`.`article_id` = 187 AND `AddfilesArticle`.`addfile_id` = `Addfile`.`id`) 000
6SELECT `Article`.`slug`, `Article`.`title`, `Site`.`id`, `Site`.`name`, `Site`.`slug` FROM `sql_cakephp`.`articles` AS `Article` LEFT JOIN `sql_cakephp`.`sites` AS `Site` ON (`Article`.`site_id` = `Site`.`id`) WHERE `Article`.`site_id` = 5 AND `Article`.`active` = 1 ORDER BY `created` ASC440
7SELECT `Article`.`id`, `Article`.`site_id`, `Article`.`showdate`, `Article`.`title`, `Article`.`active`, `Article`.`author`, `Article`.`slug`, `Article`.`hints`, `Article`.`content`, `Article`.`code`, `Article`.`idea`, `Article`.`database`, `Article`.`boulid`, `Article`.`backend`, `Article`.`frontend`, `Article`.`layout`, `Article`.`created`, `Article`.`modified`, `Site`.`id`, `Site`.`type`, `Site`.`name`, `Site`.`slug`, `Site`.`hints`, `Site`.`title`, `Site`.`desc`, `Site`.`info` FROM `sql_cakephp`.`articles` AS `Article` LEFT JOIN `sql_cakephp`.`sites` AS `Site` ON (`Article`.`site_id` = `Site`.`id`) WHERE `site_id` = 3 AND `active` = 1 ORDER BY rand() ASC LIMIT 3331
8SELECT `Photo`.`id`, `ArticlesPhoto`.`photo_id`, `ArticlesPhoto`.`article_id` FROM `sql_cakephp`.`photos` AS `Photo` JOIN `sql_cakephp`.`articles_photos` AS `ArticlesPhoto` ON (`ArticlesPhoto`.`article_id` IN (174, 172, 184) AND `ArticlesPhoto`.`photo_id` = `Photo`.`id`) 330
9SELECT `Addfile`.`id`, `Addfile`.`title`, `Addfile`.`name`, `Addfile`.`hints`, `Addfile`.`size`, `Addfile`.`type`, `AddfilesArticle`.`addfile_id`, `AddfilesArticle`.`article_id` FROM `sql_cakephp`.`addfiles` AS `Addfile` JOIN `sql_cakephp`.`addfiles_articles` AS `AddfilesArticle` ON (`AddfilesArticle`.`article_id` IN (174, 172, 184) AND `AddfilesArticle`.`addfile_id` = `Addfile`.`id`) 000
10UPDATE `sql_cakephp`.`articles` AS `Article` LEFT JOIN `sql_cakephp`.`sites` AS `Site` ON (`Article`.`site_id` = `Site`.`id`) SET `Article`.`hints` = Article.hints + 1 WHERE `Article`.`id` = 187110
11SELECT `Site`.`name`, `Site`.`slug` FROM `sql_cakephp`.`sites` AS `Site` WHERE 1 = 1660
12SELECT `Sitesetting`.`id`, `Sitesetting`.`top`, `Sitesetting`.`place`, `Sitesetting`.`adress`, `Sitesetting`.`worktime`, `Sitesetting`.`phone`, `Sitesetting`.`mobile`, `Sitesetting`.`email`, `Sitesetting`.`regon`, `Sitesetting`.`licencja`, `Sitesetting`.`nip` FROM `sql_cakephp`.`sitesettings` AS `Sitesetting` WHERE 1 = 1 LIMIT 1110