Πρότυπα

Το Nette χρησιμοποιεί το σύστημα προτύπων Latte. Αφενός επειδή είναι το πιο ασφαλές σύστημα προτύπων για PHP, και αφετέρου το πιο διαισθητικό σύστημα. Δεν χρειάζεται να μάθετε πολλά νέα πράγματα, αρκεί η γνώση της PHP και μερικών ετικετών.

Είναι σύνηθες μια σελίδα να αποτελείται από ένα πρότυπο διάταξης + το πρότυπο της συγκεκριμένης action. Έτσι μπορεί να μοιάζει ένα πρότυπο διάταξης, παρατηρήστε τα μπλοκ {block} και την ετικέτα {include}:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

Και αυτό θα είναι το πρότυπο της action:

{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}

Αυτό ορίζει το μπλοκ content, το οποίο εισάγεται στη θέση του {include content} στη διάταξη, και επίσης επαναπροσδιορίζει το μπλοκ title, το οποίο αντικαθιστά το {block title} στη διάταξη. Προσπαθήστε να φανταστείτε το αποτέλεσμα.

Αναζήτηση προτύπου

Δεν χρειάζεται να καθορίσετε στους presenters ποιο πρότυπο πρέπει να αποδοθεί, το framework θα βρει τη διαδρομή μόνο του και θα σας γλιτώσει από το γράψιμο.

Αν χρησιμοποιείτε μια δομή καταλόγων όπου κάθε presenter έχει τον δικό του κατάλογο, απλά τοποθετήστε το πρότυπο σε αυτόν τον κατάλογο με το όνομα της action (ή του view), δηλαδή για την action default χρησιμοποιήστε το πρότυπο default.latte:

app/
└── Presentation/
    └── Home/
        ├── HomePresenter.php
        └── default.latte

Αν χρησιμοποιείτε μια δομή όπου οι presenters βρίσκονται μαζί σε έναν κατάλογο και τα πρότυπα στον φάκελο templates, αποθηκεύστε το είτε στο αρχείο <Presenter>.<view>.latte είτε στο <Presenter>/<view>.latte:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1η παραλλαγή
        └── Home/
            └── default.latte   ← 2η παραλλαγή

Ο κατάλογος templates μπορεί επίσης να βρίσκεται ένα επίπεδο πιο πάνω, δηλαδή στο ίδιο επίπεδο με τον κατάλογο με τις κλάσεις των presenters.

Αν το πρότυπο δεν βρεθεί, ο presenter απαντά με σφάλμα 404 – η σελίδα δεν βρέθηκε.

Μπορείτε να αλλάξετε το view χρησιμοποιώντας το $this->setView('jineView'). Μπορείτε επίσης να καθορίσετε απευθείας το αρχείο με το πρότυπο χρησιμοποιώντας το $this->template->setFile('/path/to/template.latte').

Τα αρχεία όπου αναζητούνται τα πρότυπα μπορούν να αλλάξουν αντικαθιστώντας τη μέθοδο formatTemplateFiles(), η οποία επιστρέφει έναν πίνακα πιθανών ονομάτων αρχείων.

Αναζήτηση προτύπου διάταξης

Το Nette αναζητά επίσης αυτόματα το αρχείο με τη διάταξη.

Αν χρησιμοποιείτε μια δομή καταλόγων όπου κάθε presenter έχει τον δικό του κατάλογο, τοποθετήστε τη διάταξη είτε στον φάκελο με τον presenter, αν είναι συγκεκριμένη μόνο γι' αυτόν, είτε ένα επίπεδο πιο πάνω, αν είναι κοινή για πολλούς presenters:

app/
└── Presentation/
    ├── @layout.latte           ← κοινή διάταξη
    └── Home/
        ├── @layout.latte       ← μόνο για τον presenter Home
        ├── HomePresenter.php
        └── default.latte

Αν χρησιμοποιείτε μια δομή όπου οι presenters βρίσκονται μαζί σε έναν κατάλογο και τα πρότυπα στον φάκελο templates, η διάταξη θα αναμένεται σε αυτές τις θέσεις:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← κοινή διάταξη
        ├── Home.@layout.latte  ← μόνο για Home, 1η παραλλαγή
        └── Home/
            └── @layout.latte   ← μόνο για Home, 2η παραλλαγή

Αν ο presenter βρίσκεται σε ένα module, η αναζήτηση θα γίνει και σε περαιτέρω επίπεδα καταλόγων, ανάλογα με την ένθεση του module.

Το όνομα της διάταξης μπορεί να αλλάξει χρησιμοποιώντας το $this->setLayout('layoutAdmin') και τότε θα αναμένεται στο αρχείο @layoutAdmin.latte. Μπορείτε επίσης να καθορίσετε απευθείας το αρχείο με το πρότυπο διάταξης χρησιμοποιώντας το $this->setLayout('/path/to/template.latte').

Χρησιμοποιώντας το $this->setLayout(false) ή την ετικέτα {layout none} μέσα στο πρότυπο, η αναζήτηση διάταξης απενεργοποιείται.

Τα αρχεία όπου αναζητούνται τα πρότυπα διάταξης μπορούν να αλλάξουν αντικαθιστώντας τη μέθοδο formatLayoutTemplateFiles(), η οποία επιστρέφει έναν πίνακα πιθανών ονομάτων αρχείων.

Μεταβλητές στο πρότυπο

Μεταβιβάζουμε μεταβλητές στο πρότυπο γράφοντάς τες στο $this->template και στη συνέχεια τις έχουμε διαθέσιμες στο πρότυπο ως τοπικές μεταβλητές:

$this->template->article = $this->articles->getById($id);

Με αυτόν τον απλό τρόπο, μπορούμε να μεταβιβάσουμε οποιεσδήποτε μεταβλητές στα πρότυπα. Ωστόσο, κατά την ανάπτυξη στιβαρών εφαρμογών, είναι συνήθως πιο χρήσιμο να περιοριστούμε. Για παράδειγμα, ορίζοντας ρητά μια λίστα μεταβλητών που αναμένει το πρότυπο και τους τύπους τους. Χάρη σε αυτό, η PHP θα μπορεί να ελέγχει τους τύπους, το IDE να προτείνει σωστά και η στατική ανάλυση να εντοπίζει σφάλματα.

Και πώς ορίζουμε μια τέτοια λίστα; Απλά με τη μορφή μιας κλάσης και των ιδιοτήτων της. Την ονομάζουμε παρόμοια με τον presenter, απλώς με το Template στο τέλος:

/**
 * @property-read ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
	public Model\Article $article;
	public Nette\Security\User $user;

	// και άλλες μεταβλητές
}

Το αντικείμενο $this->template στον presenter θα είναι τώρα μια παρουσία της κλάσης ArticleTemplate. Έτσι, η PHP θα ελέγχει τους δηλωμένους τύπους κατά την εγγραφή. Και από την έκδοση PHP 8.2, θα προειδοποιεί και για εγγραφή σε ανύπαρκτη μεταβλητή, σε προηγούμενες εκδόσεις το ίδιο μπορεί να επιτευχθεί χρησιμοποιώντας το trait Nette\SmartObject.

Η σχολιαστική παρατήρηση @property-read προορίζεται για το IDE και τη στατική ανάλυση, χάρη σε αυτήν θα λειτουργεί η αυτόματη συμπλήρωση, βλ. PhpStorm and code completion for $this⁠-⁠>⁠template.

Μπορείτε να απολαύσετε την πολυτέλεια της αυτόματης συμπλήρωσης και στα πρότυπα, αρκεί να εγκαταστήσετε το plugin για το Latte στο PhpStorm και να αναφέρετε στην αρχή του προτύπου το όνομα της κλάσης, περισσότερα στο άρθρο Latte: πώς να χρησιμοποιήσετε το σύστημα τύπων:

{templateType App\Presentation\Article\ArticleTemplate}
...

Έτσι λειτουργούν και τα πρότυπα στα components, αρκεί απλώς να τηρήσετε τη σύμβαση ονοματοδοσίας και για ένα component π.χ. FifteenControl να δημιουργήσετε μια κλάση προτύπου FifteenTemplate.

Αν χρειαστεί να δημιουργήσετε το $template ως παρουσία μιας άλλης κλάσης, χρησιμοποιήστε τη μέθοδο createTemplate():

public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}

Προεπιλεγμένες μεταβλητές

Οι presenters και τα components μεταβιβάζουν αυτόματα αρκετές χρήσιμες μεταβλητές στα πρότυπα:

  • $basePath είναι η απόλυτη διαδρομή URL προς τον ριζικό κατάλογο (π.χ. /eshop)
  • $baseUrl είναι η απόλυτη URL προς τον ριζικό κατάλογο (π.χ. http://localhost/eshop)
  • $user είναι το αντικείμενο που αντιπροσωπεύει τον χρήστη
  • $presenter είναι ο τρέχων presenter
  • $control είναι το τρέχον component ή presenter
  • $flashes πίνακας μηνυμάτων που στάλθηκαν από τη συνάρτηση flashMessage()

Αν χρησιμοποιείτε τη δική σας κλάση προτύπου, αυτές οι μεταβλητές μεταβιβάζονται αν δημιουργήσετε μια ιδιότητα γι' αυτές.

Δημιουργία συνδέσμων

Στο πρότυπο, οι σύνδεσμοι προς άλλους presenters & actions δημιουργούνται με αυτόν τον τρόπο:

<a n:href="Product:show">λεπτομέρεια προϊόντος</a>

Το attribute n:href είναι πολύ χρήσιμο για τις ετικέτες HTML <a>. Αν θέλουμε να εμφανίσουμε έναν σύνδεσμο αλλού, για παράδειγμα σε κείμενο, χρησιμοποιούμε το {link}:

Η διεύθυνση είναι: {link Home:default}

Περισσότερες πληροφορίες θα βρείτε στο κεφάλαιο Δημιουργία συνδέσμων URL.

Προσαρμοσμένα φίλτρα, ετικέτες κ.λπ.

Το σύστημα προτύπων Latte μπορεί να επεκταθεί με προσαρμοσμένα φίλτρα, συναρτήσεις, ετικέτες κ.λπ. Αυτό μπορεί να γίνει απευθείας στη μέθοδο render<View> ή beforeRender():

public function beforeRender(): void
{
	// προσθήκη φίλτρου
	$this->template->addFilter('foo', /* ... */);

	// ή διαμορφώνουμε απευθείας το αντικείμενο Latte\Engine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Το Latte στην έκδοση 3 προσφέρει έναν πιο προηγμένο τρόπο, δημιουργώντας μια extension για κάθε διαδικτυακό έργο. Ένα αποσπασματικό παράδειγμα μιας τέτοιας κλάσης:

namespace App\Presentation\Accessory;

final class LatteExtension extends Latte\Extension
{
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {
	}

	public function getFilters(): array
	{
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...
		];
	}

	public function getFunctions(): array
	{
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...
		];
	}

	// ...
}

Την καταχωρούμε χρησιμοποιώντας τη διαμόρφωση:

latte:
	extensions:
		- App\Presentation\Accessory\LatteExtension

Μετάφραση

Αν προγραμματίζετε μια πολύγλωσση εφαρμογή, πιθανότατα θα χρειαστεί να εμφανίσετε ορισμένα κείμενα στο πρότυπο σε διαφορετικές γλώσσες. Το Nette Framework ορίζει γι' αυτόν τον σκοπό ένα interface για τη μετάφραση Nette\Localization\Translator, το οποίο έχει μία μόνο μέθοδο translate(). Αυτή δέχεται το μήνυμα $message, το οποίο συνήθως είναι μια συμβολοσειρά, και οποιεσδήποτε άλλες παραμέτρους. Ο στόχος είναι να επιστρέψει τη μεταφρασμένη συμβολοσειρά. Στο Nette δεν υπάρχει προεπιλεγμένη υλοποίηση, μπορείτε να επιλέξετε ανάλογα με τις ανάγκες σας από πολλές έτοιμες λύσεις που θα βρείτε στο Componette. Στην τεκμηρίωσή τους θα μάθετε πώς να διαμορφώσετε τον translator.

Στα πρότυπα μπορεί να οριστεί ένας μεταφραστής, τον οποίο ζητάμε να μας περάσει, με τη μέθοδο setTranslator():

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}

Ο Translator μπορεί εναλλακτικά να οριστεί χρησιμοποιώντας τη διαμόρφωση:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

Στη συνέχεια, ο μεταφραστής μπορεί να χρησιμοποιηθεί, για παράδειγμα, ως φίλτρο |translate, συμπεριλαμβανομένων των συμπληρωματικών παραμέτρων που μεταβιβάζονται στη μέθοδο translate() (βλ. foo, bar):

<a href="basket">{='Καλάθι'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>

Ή ως ετικέτα με κάτω παύλα:

<a href="basket">{_'Καλάθι'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>

Για τη μετάφραση ενός τμήματος του προτύπου, υπάρχει η ζευγαρωτή ετικέτα {translate} (από το Latte 2.11, παλαιότερα χρησιμοποιούνταν η ετικέτα {_}):

<a href="order">{translate}Παραγγελία{/translate}</a>
<a href="order">{translate foo, bar}Παραγγελία{/translate}</a>

Ο Translator καλείται κανονικά κατά το χρόνο εκτέλεσης κατά την απόδοση του προτύπου. Ωστόσο, το Latte έκδοση 3 μπορεί να μεταφράσει όλα τα στατικά κείμενα ήδη κατά τη μεταγλώττιση του προτύπου. Αυτό εξοικονομεί απόδοση, επειδή κάθε συμβολοσειρά μεταφράζεται μόνο μία φορά και η τελική μετάφραση γράφεται στη μεταγλωττισμένη μορφή. Έτσι, στον κατάλογο cache δημιουργούνται πολλαπλές μεταγλωττισμένες εκδόσεις του προτύπου, μία για κάθε γλώσσα. Γι' αυτό, αρκεί απλώς να αναφέρετε τη γλώσσα ως δεύτερη παράμετρο:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}

Στατικό κείμενο σημαίνει, για παράδειγμα, {_'hello'} ή {translate}hello{/translate}. Μη στατικά κείμενα, όπως για παράδειγμα {_$foo}, θα συνεχίσουν να μεταφράζονται κατά το χρόνο εκτέλεσης.

έκδοση: 4.0