Loadery

Loadery to mechanizm, za pomocą którego Latte pobiera kod źródłowy Twoich szablonów. Najczęściej szablony są plikami na dysku, ale elastyczny system loaderów w Latte pozwala wczytywać je praktycznie skądkolwiek, a nawet generować je dynamicznie.

Czym jest loader?

Zwykle, pracując z szablonami, myślisz o plikach .latte leżących w strukturze katalogów Twojego projektu. Zajmuje się tym domyślny FileLoader Latte. Powiązanie między nazwą szablonu (jak 'main.latte' czy 'components/card.latte') a jego rzeczywistym kodem źródłowym nie musi jednak być bezpośrednim odwzorowaniem na ścieżkę pliku.

I tu wkraczają loadery. Loader to obiekt odpowiedzialny za to, aby na podstawie nazwy szablonu (łańcucha identyfikującego) dostarczyć Latte jego kod źródłowy. Latte polega w tym zadaniu całkowicie na skonfigurowanym loaderze. Dotyczy to nie tylko początkowego szablonu żądanego przez $latte->render('main.latte'), ale też każdego szablonu przywoływanego wewnątrz tagami takimi jak {include ...}, {layout ...}, {embed ...} czy {import ...}.

Po co własny loader?

  • Wczytywanie z innych źródeł: pobieranie szablonów przechowywanych w bazie danych, w cache (jak Redis czy Memcached), w systemie kontroli wersji (jak Git, na podstawie konkretnego commita) albo generowanych dynamicznie.
  • Wprowadzenie własnych konwencji nazewniczych: możesz chcieć używać krótszych aliasów szablonów albo zaimplementować własną logikę wyszukiwania ścieżek (np. najpierw szukać w katalogu motywu, a potem sięgnąć do katalogu domyślnego).
  • Dodanie zabezpieczeń lub kontroli dostępu: własny loader mógłby przed wczytaniem pewnych szablonów sprawdzać uprawnienia użytkownika.
  • Wstępne przetwarzanie: choć ogólnie się to odradza (compiler passy są lepsze), loader mógłby teoretycznie wstępnie przetworzyć treść szablonu, zanim przekaże ją Latte.

Loader dla instancji Latte\Engine ustawiasz metodą setLoader():

$latte = new Latte\Engine;

// używamy domyślnego FileLoadera dla plików w '/path/to/templates'
$loader = new Latte\Loaders\FileLoader('/path/to/templates');
$latte->setLoader($loader);

Loader musi implementować interfejs Latte\Loader.

Wbudowane loadery

Latte oferuje kilka standardowych loaderów:

FileLoader

To domyślny loader używany przez klasę Latte\Engine, jeśli nie podano innego. Wczytuje szablony bezpośrednio z systemu plików.

Opcjonalnie możesz ustawić katalog główny, aby ograniczyć dostęp:

use Latte\Loaders\FileLoader;

// poniższe pozwoli wczytywać szablony tylko z katalogu /var/www/html/templates
$loader = new FileLoader('/var/www/html/templates');
$latte->setLoader($loader);

// $latte->render('../../../etc/passwd'); // to zgłosiłoby wyjątek

// renderowanie szablonu leżącego w /var/www/html/templates/pages/contact.latte
$latte->render('pages/contact.latte');

Przy użyciu tagów takich jak {include} czy {layout} rozwiązuje nazwy szablonów względem bieżącego szablonu, o ile nie podano ścieżki bezwzględnej. Jeśli jednak ustawiono katalog główny, wszystkie nazwy są rozwiązywane względem bieżącego szablonu.

StringLoader

Ten loader pobiera treść szablonów z tablicy asocjacyjnej, w której kluczami są nazwy szablonów (identyfikatory), a wartościami łańcuchy z kodem źródłowym szablonu. Przydaje się zwłaszcza przy testach albo w niewielkich aplikacjach, w których szablony mogą być przechowywane bezpośrednio w kodzie PHP.

use Latte\Loaders\StringLoader;

$loader = new StringLoader([
	'main.latte' => 'Hello {$name}, include is below:{include helper.latte}',
	'helper.latte' => '{var $x = 10}Included content: {$x}',
	// w razie potrzeby dodaj kolejne szablony
]);

$latte->setLoader($loader);

$latte->render('main.latte', ['name' => 'World']);
// wynik: Hello World, include is below:Included content: 10

Jeśli potrzebujesz wyrenderować tylko jeden szablon bezpośrednio z łańcucha, bez includów ani dziedziczenia odwołującego się do innych nazwanych szablonów tekstowych, możesz przy użyciu StringLoader bez tablicy przekazać łańcuch bezpośrednio do metody render() albo renderToString():

$loader = new StringLoader;
$latte->setLoader($loader);

$templateString = 'Hello {$name}!';
$output = $latte->renderToString($templateString, ['name' => 'Alice']);
// $output zawiera 'Hello Alice!'

Tworzenie własnego loadera

Aby utworzyć własny loader (np. do wczytywania szablonów z bazy danych, cache, systemu kontroli wersji albo innego źródła), musisz stworzyć klasę implementującą interfejs Latte\Loader.

Zobaczmy, co ma robić każda z metod.

getContent (string $name)string

To główna metoda loadera. Jej zadaniem jest pobranie i zwrócenie pełnego kodu źródłowego szablonu identyfikowanego przez $name (przekazanego do metody $latte->render() albo zwróconego przez metodę getReferredName()).

Jeśli szablonu nie da się znaleźć ani do niego sięgnąć, ta metoda musi zgłosić Latte\TemplateNotFoundException.

public function getContent(string $name): string
{
	// przykład: wczytanie z hipotetycznego magazynu wewnętrznego
	$content = $this->storage->read($name);
	if ($content === null) {
		throw new Latte\TemplateNotFoundException("Template '$name' cannot be loaded.");
	}
	return $content;
}

getReferredName (string $name, string $referringName)string

Ta metoda obsługuje rozwiązywanie nazw szablonów używanych w tagach takich jak {include}, {layout} itd. Gdy Latte napotka na przykład {include 'partial.latte'} wewnątrz main.latte, wywoła tę metodę z $name = 'partial.latte' i $referringName = 'main.latte'.

Zadaniem metody jest rozwiązanie $name w kanoniczny identyfikator (np. ścieżkę bezwzględną, unikalny klucz w bazie danych), który zostanie użyty przy wywoływaniu pozostałych metod loadera, na podstawie kontekstu podanego w $referringName.

public function getReferredName(string $name, string $referringName): string
{
	return ...;
}

getUniqueId (string $name)string

Dla poprawy wydajności Latte korzysta z cache skompilowanych szablonów. Każdy plik skompilowanego szablonu potrzebuje unikalnej nazwy wyprowadzonej z identyfikatora szablonu źródłowego. Ta metoda dostarcza łańcuch, który jednoznacznie identyfikuje szablon $name.

Dla szablonów opartych na plikach może do tego posłużyć ścieżka bezwzględna. Dla szablonów w bazie danych typowa jest kombinacja prefiksu i identyfikatora w bazie.

public function getUniqueId(string $name): string
{
	return ...;
}

Przykład: prosty loader bazodanowy

Ten przykład pokazuje podstawową strukturę loadera wczytującego szablony przechowywane w tabeli bazy danych templates z kolumnami name (unikalny identyfikator), content i updated_at.

use Latte;

class DatabaseLoader implements Latte\Loader
{
	public function __construct(
		private \PDO $db,
	) {
	}

	public function getContent(string $name): string
	{
		$stmt = $this->db->prepare('SELECT content FROM templates WHERE name = ?');
		$stmt->execute([$name]);
		$content = $stmt->fetchColumn();
		if ($content === false) {
			throw new Latte\TemplateNotFoundException("Template '$name' not found in database.");
		}
		return $content;
	}

	// ten prosty przykład zakłada, że nazwy szablonów ('homepage', 'article' itd.)
	// są unikalnymi ID i że szablony nie odwołują się do siebie względnie
	public function getReferredName(string $name, string $referringName): string
	{
		return $name;
	}

	public function getUniqueId(string $name): string
	{
		// prefiks razem z samą nazwą jest tu unikalny i wystarczający
		return 'db_' . $name;
	}
}

// użycie:
$pdo = new \PDO(/* connection details */);
$loader = new DatabaseLoader($pdo);
$latte->setLoader($loader);
$latte->render('homepage'); // wczyta z bazy szablon o nazwie 'homepage'

Własne loadery dają Ci pełną kontrolę nad tym, skąd pochodzą Twoje szablony Latte, i pozwalają na integrację z rozmaitymi systemami magazynowania i sposobami pracy.

wersja: 3.x