Tworzenie aplikacji „Zakładki” (część 1)

cakephpPrzykładowa aplikacja to prosty system zakładek. Tworzenie aplikacji podzielone jest na etapy: instalacja CakePHP, tworzenie bazy danych i wykorzystanie narzędzi do automatycznej generacji kodu dostępnych w CakePHP.

Do utworzenia przykładu potrzebne będą:

  1. Serwer baz danych MySql i podstawowa wiedza dotycząca utworzenia nowej bazy. Znajomość języka SQL nie jest niezbędna, ponieważ framework zadba o komunikację z bazą.
  2. Nie jest wymagany serwer WWW, ponieważ wykorzystamy serwer wewnętrzny.
  3. Podstawowa wiedza dotycząca PHP.

Zanim rozpoczniesz, przejdź do konsoli windows i sprawdź wersję PHP komendą: php -v

Musisz posiadać PHP w wersji co najmniej 5.5.9 lub wyższą.

Następnie pobierz framework CakePHP. Przydadzą się następujące instrukcje:

  1. Instalacja PHP (jeżeli jeszcze nie posiadasz)
  2. Instalacja Composera (jeżeli nie posiadasz)
  3. Instalacja CakePHP (jeżeli nie pobrałeś wcześniej)

Aby zainstalować CakePHP za pomocą Composera, użyj komendy:

php composer.phar create-project –prefer-dist cakephp/app bookmarker

Po prawidłowym zainstalowaniu CakePHP, przejdź do katalogu w którym zainstalowany jest cake i uruchom serwer wewnętrzny komendą:

bin/cake server

Jeśli uruchomienie się powiedzie, uruchom stronę w przeglądarce wpisując w pasek adresu: http://localhost:8765

Tworzenie bazy danych

Utwórz bazę danych o nazwie cake_bookmarks i uruchom na niej skrypt tworzący tabele:

CREATE TABLE users (
 id INT AUTO_INCREMENT PRIMARY KEY,
 email VARCHAR(255) NOT NULL,
 password VARCHAR(255) NOT NULL,
 created DATETIME,
 modified DATETIME
 );
 
CREATE TABLE bookmarks (
 id INT AUTO_INCREMENT PRIMARY KEY,
 user_id INT NOT NULL,
 title VARCHAR(50),
 description TEXT,
 url TEXT,
 created DATETIME,
 modified DATETIME,
 FOREIGN KEY user_key (user_id) REFERENCES users(id)
 );
 
CREATE TABLE tags (
 id INT AUTO_INCREMENT PRIMARY KEY,
 title VARCHAR(255),
 created DATETIME,
 modified DATETIME,
 UNIQUE KEY (title)
 );
 
CREATE TABLE bookmarks_tags (
 bookmark_id INT NOT NULL,
 tag_id INT NOT NULL,
 PRIMARY KEY (bookmark_id, tag_id),
 FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
 FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
 );

Można zauważyć, że tabela bookmarks_tags wykorzystuje złożony klucz podstawowy. CakePHP wspiera złożone klucze główne niemal wszędzie, dzięki temu, łatwiej aplikacje obsługujące wielu użytkowników.

Nazwy tabel i kolumn nazwy użyliśmy nie są dowolne. Dzięki zastosowaniu konwencji nazewnictwa CakePHP, można wykorzystać CakePHP i uniknąć konieczności konfigurowania frameworka. CakePHP jest wystarczająco elastyczny, by pomieścić nawet niespójne schematy bazy danych starszego typu, ale stosując się do konwencji, pozwoli Ci to zaoszczędzić czas.

Konfiguracja bazy danych

Następnym krokiem jest konfiguracja połączenia z bazą danych. Jest to pierwsza i jednocześnie ostatnia konieczność konfigurowania czegokolwiek w projekcie.

Konfiguracja jest dość prosta: wystarczy zmienić wartości w tablicy Datasources.default w pliku config/app.php na parametry, które odnoszą się do serwera SQL i nowo utworzonej bazy danych. Konfiguracja może wyglądać mniej więcej tak:

return [
// More configuration above.
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'nazwa_uzytkownika',
'password' => 'haslo',
'database' => 'cake_bookmarks',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
],
],
];

Zawsze gdy zapiszesz zmiany w pliku konfiguracyjnym config/app.php, spróbuj odświeżyć stronę aby sprawdzić, czy CakePHP łączy się poprawnie.

Automatyczna generacja kodu (Scaffolding)

Dzięki temu, że nasza baza danych została utworzona z zachowaniem konwencji nazewnictwa CakePHP, można użyć narzędzia do automatycznego tworzenia kodu by szybko wygenerować aplikację. Uruchom konsolę Windows i wpisz następujące komendy:

// będąc w katalogu aplikacji wpisz polecenia.
bin/cake bake all users
bin/cake bake all bookmarks
bin/cake bake all tags

Powyższe polecenia wygenerują automatycznie kontrolery, modele, widoki i przypadki testowe. Teraz można otworzyć witrynę w przeglądarce pod adresem http://localhost:8765/bookmarks.

Powinieneś ujrzeć prostą aplikację pozwalającą na modyfikację rekordów w bazie danych. Jest lista zakładek, w tym – dodawanie, użytkowników, zakładek czy tagów.

Jeśli wyświetliła się strona błędu Not Found (404), sprawdź, czy jest włączony moduł mod_rewrite w serwerze Apache.

Dodanie funkcjonalności haszowania hasła

Kiedy będziesz tworzyć użytkowników w aplikacji (http://localhost:8765/users), prawdopodobnie otrzymasz komunikat o ryzyku przechowywania hasła zapisanego zwykłym tekstem. Jest to bardzo złe dla bezpieczeństwa, stąd należy tonaprawić.

Jest to także dobry czas by porozmawiać o warstwie modelu w CakePHP. Separacja kodu w CakePHP oddziela także metody operujące na kolekcji obiektów oraz tworzy pojedyncze obiekty w różnych klasach. Metody, które operują na kolekcji encji są umieszczone w klasie Table, zaś cechy rekordu znajdują się w klasie Entity.

Dla przykładu haszowanie hasła rozwiązywane jest poprzez behavior obiektu entity. Ponieważ chcemy, by haszowanie hasła odbywało się zawsze, gdy zostanie ono na nowo wpisane do bazy, musimy użyć metody mutator/setter. CakePHP będzie wywoływał metodę konwersji za każdym razem, gdy właściwość ta będzie ustawiona w encji. Dodajmy więc  setter dla hasła w pliku src/Model/Entity/User.php dodając wiersze:

namespace App\Model\Entity;
 
use Cake\Auth\DefaultPasswordHasher; //dopisz tę linię!
use Cake\ORM\Entity;
 
class User extends Entity
 {
protected function _setPassword($value)
  {
   $hasher = new DefaultPasswordHasher();
   return $hasher->hash($value);
  }
 }

Teraz zapisz zmiany i ponownie zaktualizuj hasło użytkownika. CakePHP zahaszuje hasło domyślnie przy pomocy bcrypt. Możesz także użyć metod sha1 lub md5. Teraz hasło będzie zawsze przechowywane w bazie danych w postaci bezpiecznej.

Pobieranie zakładki ze specyficznym tagiem

Najpierw należy załadować kolekcję zakładek, by móc je wyszukiwać po tagach. Następnie trzeba zaimplementować routing, akcję kontrolera oraz metodę wyszukiwania zakładek na podstawie tagu.

Idealnie było by, gdyby URL wyglądał następująco http://localhost:8765/bookmarks/tagged/funny/cat/gifs. Taki adres pozwoli to odnaleźć wszystkie zakładki z tagami ‘funny’, ‘cat’ or ‘gifs’. Aby dodać metodę wyszukiwania, należy wcześniej zdefiniować routing. Zmodyfikuj więc plik config/routes.php jak poniżej:

<?php
 use Cake\Routing\Route\DashedRoute;
 use Cake\Routing\Router;
 
Router::defaultRouteClass(DashedRoute::class);
 
// Teraz dodamy routing dodający akcję dla naszych tagów.
// Wpis `*` informuje CakePHP, że taka akcja przyjmuje parametry
 Router::scope(
 '/bookmarks',
 ['controller' => 'Bookmarks'],
 function ($routes) {
 $routes->connect('/tagged/*', ['action' => 'tags']);
 }
 );
 
Router::scope('/', function ($routes) {
 // Connect the default home and /pages/* routes.
 $routes->connect('/', [
 'controller' => 'Pages',
 'action' => 'display', 'home'
 ]);
 $routes->connect('/pages/*', [
 'controller' => 'Pages',
 'action' => 'display'
 ]);
 
// Connect the conventions based default routes.
 $routes->fallbacks();
 });

Powyższe wpisy definiują nowy routing łączący ścieżkę /bookmarks/tagged/ z BookmarksController::tags(). Definiując routing możesz  wyizolować jak ma wyglądać  URLs od tego, jak został on zaimplmentowany. Otwórz stronę  http://localhost:8765/bookmarks/tagged. Spowoduje wyświetlenie strony z błędem o braku kontrolera. Utwórz więc nową metodę w src/Controller/BookmarksController.php jak na źródle poniżej:

public function tags()
 {
 // The 'pass' key is provided by CakePHP and contains all
 // the passed URL path segments in the request.
 $tags = $this->request->params['pass'];
 
// Use the BookmarksTable to find tagged bookmarks.
 $bookmarks = $this->Bookmarks->find('tagged', [
 'tags' => $tags
 ]);
 
// Pass variables into the view template context.
 $this->set([
 'bookmarks' => $bookmarks,
 'tags' => $tags
 ]);
 }

Tworzenie metody Finder

Programując w CakePHP, należy dbać, by akcje kontrolera były jak najkrótsze, zaś logikę aplikacji umieszczać w modelach. Jeśli wpiszesz w przeglądarkę URL /bookmarks/tagged zobaczysz błąd informujący o braku metody findTagged(), stąd w pliku  src/Model/Table/BookmarksTable.php dodaj teraz tę metodę:

// The $query argument is a query builder instance.
 // The $options array will contain the 'tags' option we passed
 // to find('tagged') in our controller action.
 public function findTagged(Query $query, array $options)
 {
 return $this->find()
 ->distinct(['Bookmarks.id'])
 ->matching('Tags', function ($q) use ($options) {
 if (empty($options['tags'])) {
 return $q->where(['Tags.title IS' => null]);
 }
 return $q->where(['Tags.title IN' => $options['tags']]);
 });
 }

Zaimplementowaliśmy własną metodę find() method. Koncepcją CakePHP jest możliwość ponownego używania pakietów. Metoda Finder zawsze pobiera obiekt typu Query Builder object i przyjmuje parametry z postaci tablicy. Finders może manipulować zapytaniem dodając wymagane kryteria czy warunki. Jeśli zapytanie do bazy zostanie zakończone, metody finder muszą zwrócić zmodyfikowany obiekt zapytania. W naszym finderze mamy metody distinct() oraz matching() pozwalające znaleźć te zakładki,  które posiadają dopasowany tag. Metoda matching() przyjmuje anonimową funkcję, która jako argument przyjmuje query builder. Wewnątrz callback używamy query builder aby zdefiniować filtr zakładek dla specyficznych tagów.

Tworzenie widoku

Teraz otwórz URL /bookmarks/tagged i ujrzysz kolejny błąd informujący o braku widoku. Należy więc zbudować nowy widok dla akcji tags(). W pliku src/Template/Bookmarks/tags.ctp wpisz następującą zawartość:

<h1>
 Bookmarks tagged with
 <?= $this->Text->toList($tags) ?>
 </h1>
 
<section>
 <?php foreach ($bookmarks as $bookmark): ?>
 <article>
 <!-- Use the HtmlHelper to create a link -->
 <h4><?= $this->Html->link($bookmark->title, $bookmark->url) ?></h4>
 <small><?= h($bookmark->url) ?></small>
 
<!-- Use the TextHelper to format text -->
 <?= $this->Text->autoParagraph($bookmark->description) ?>
 </article>
 <?php endforeach; ?>
 </section>

Powyższy kod używa helpery (pomocników) Html i Text do skojarzania danych i generowania widoków. Użyliśmy także funkcji skrótów h do kodowania kodu HTML. Zapamiętaj, by zawsze używać funkcji h() podczas generowania widoku HTML, by zabezpieczyć się przed atakiem na stronę.

Plik tags.ctp, który właśnie utworzyliśmy, jest zgodny z konwencją CakePHP dla widoków, która wymaga, by nazwa pliku widoku zaczynała się od małej litery i była nazwą akcji kontrolera.

Prosze zwrócić uwagę na użycie zmiennych $tags i $bookmarks w widoku. Kiedy używamy w kontrolerze metody set(), przekazujemy poszczególne zmienne dla widoku. Widok zaś przekształca dostępne dla widoku zmienne jako zmienne lokalne.

Teraz możesz odwiedzić adres URL /bookmarks/tagged/funny i zobaczyć wszystkie zakładki otagowane jako ‘funny’.

Aplikacja do zarządzania zakładkami, tagami i użytkownikami została ukończona, jednakże każdy może zobaczyć dane wszystkich użytkowników. W kolejnej części zaimplementujemy autentykację oraz wprowadzimy restrykcje, które ograniczą widoczność zakładek wyłącznie należących do użytkownika, który je wpisał.

547total visits,1visits today

Tagi , , .Dodaj do zakładek Link.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

+ 72 = 81