Nette Documentation Preview

syntax
Pagina principală a blogului
****************************

.[perex]
Să creăm pagina de pornire care să afișeze postările recente.


Înainte de a începe, ar trebui să știți cel puțin câteva noțiuni de bază despre modelul de proiectare Model-View-Presenter (similar cu MVC((Model-View-Controller))):

- **Model** - strat de manipulare a datelor. Acesta este complet separat de restul aplicației. Comunică doar cu prezentatorii.

- **View** - un strat de definire frontală. Acesta redă datele solicitate utilizatorului folosind șabloane.

- **Prezentator** (sau controler) - un nivel de conexiune. Prezentatorul conectează Modelul și View. Gestionează solicitările, cere date de la Model și apoi le transmite la View curent.


În cazul unei aplicații foarte simple precum blogul nostru, stratul Model va consta de fapt doar în interogări către baza de date propriu-zisă - nu avem nevoie de cod PHP suplimentar pentru aceasta. Trebuie doar să creăm straturile Presenter și View. În Nette, fiecare Presenter are propriile sale Views, așa că vom continua cu ambele simultan.


Crearea bazei de date cu Adminer .[#toc-creating-the-database-with-adminer]
===========================================================================

Pentru a stoca datele, vom folosi baza de date MySQL, deoarece este cea mai frecventă alegere printre dezvoltatorii web. Dar dacă nu vă place, nu ezitați să folosiți o bază de date la alegere.

Să pregătim baza de date care va stoca postările blogului nostru. Putem începe foarte simplu - doar cu un singur tabel pentru postări.

Pentru a crea baza de date putem descărca [Adminer |https://www.adminer.org], sau puteți folosi un alt instrument pentru gestionarea bazelor de date.


Să deschidem Adminer și să creăm o nouă bază de date numită `quickstart`.

Creăm un tabel nou numit `posts` și adăugăm aceste coloane:
- `id` int, faceți clic pe autoincrement (AI)
- `title` varchar, lungime 255
- `content` text
- `created_at` timestamp

Ar trebui să arate astfel:

[* adminer-posts.webp *]

```sql
CREATE TABLE `posts` (
	`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`title` varchar(255) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8;
```

.[caution]
Este foarte important să folosiți stocarea tabelului **InnoDB**. Veți vedea motivul mai târziu. Deocamdată, alegeți acest lucru și trimiteți. Puteți apăsa Save (Salvare) acum.

Încercați să adăugați câteva exemple de postări pe blog înainte de a implementa capacitatea de a adăuga noi postări direct din aplicația noastră.

```sql
INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Article One',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Article Two',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Article Three',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);
```


Conectarea la baza de date .[#toc-connecting-to-the-database]
=============================================================

Acum, când baza de date este creată și avem câteva mesaje în ea, este momentul potrivit pentru a le afișa pe noua noastră pagină strălucitoare.

În primul rând, trebuie să spunem aplicației noastre ce bază de date să folosească. Configurația conexiunii la baza de date este stocată în `config/common.neon`. Setați conexiunea DSN((Data Source Name)) și acreditările. Ar trebui să arate astfel:

```neon .{file:config/common.neon}
database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *enter user name*
	password: *enter password here*
```

.[note]
Fiți atenți la indentare în timpul editării acestui fișier. [Formatul NEON |neon:format] acceptă atât spații, cât și tabulări, dar nu ambele împreună! Fișierul de configurare din proiectul web utilizează tabulatoarele în mod implicit.


Injectarea conexiunii la baza de date .[#toc-injecting-the-database-connection]
===============================================================================

Prezentatorul `HomePresenter`, care va lista articolele, are nevoie de o conexiune la baza de date. Pentru a o primi, scrieți un constructor ca acesta:

```php .{file:app/UI/Home/HomePresenter.php}
<?php
namespace App\UI\Home;

use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	// ...
}
```


Încărcarea mesajelor din baza de date .[#toc-loading-posts-from-the-database]
=============================================================================

Acum să preluăm posturile din baza de date și să le transmitem șablonului, care va reda apoi codul HTML. Pentru aceasta există așa-numita metodă *render*:

```php .{file:app/UI/Home/HomePresenter.php}
public function renderDefault(): void
{
	$this->template->posts = $this->database
		->table('posts')
		->order('created_at DESC')
		->limit(5);
}
```

Prezentatorul are acum o metodă de randare `renderDefault()` care transmite datele către o vizualizare numită `default`. Șabloanele prezentatorului pot fi găsite în `app/UI/{PresenterName}/{viewName}.latte`, astfel încât în acest caz șablonul va fi localizat în `app/UI/Home/default.latte`. În șablon, o variabilă numită `$posts` este acum disponibilă, care conține postările din baza de date.


Șablonul .[#toc-template]
=========================

Există un șablon generic pentru întreaga pagină (numit *layout*, cu antet, foi de stil, footer, ...) și apoi șabloane specifice pentru fiecare vizualizare (de exemplu, pentru afișarea listei de articole de pe blog), care pot suprascrie unele dintre părțile șablonului layout.

În mod implicit, șablonul de aspect este localizat în `app/UI/@layout.latte`, care conține:

```latte .{file:app/UI/@layout.latte}
...
{include content}
...
```

`{include content}` inserează un bloc numit `content` în șablonul principal. Îl puteți defini în șabloanele fiecărei vizualizări. În acest caz, vom edita fișierul `Home/default.latte` în felul următor:

```latte .{file:app/UI/Home/default.latte}
{block content}
	Hello World
{/block}
```

Acesta definește [blocul |latte:tags#block]*content*, care va fi inserat în aspect. Dacă reîmprospătați browserul, veți vedea o pagină cu textul "Hello world" (în codul sursă, de asemenea, cu antetul și subsolul HTML definite în `@layout.latte`).

Să afișăm articolele de pe blog - vom edita șablonul astfel:

```latte .{file:app/UI/Home/default.latte}
{block content}
	<h1>My blog</h1>

	{foreach $posts as $post}
	<div class="post">
		<div class="date">{$post->created_at|date:'j. n. Y'}</div>

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
	{/foreach}
{/block}
```

Dacă vă reîmprospătați browserul, veți vedea lista postărilor de pe blog. Lista nu este foarte fantezistă sau colorată, așa că nu ezitați să adăugați niște [CSS strălucitor |https://github.com/nette-examples/quickstart/blob/v4.0/www/css/style.css] la `www/css/style.css` și să o legați într-un layout:

```latte .{file:app/UI/@layout.latte}
	...
	<link rel="stylesheet" href="{$basePath}/css/style.css">
</head>
...
```

Eticheta `{foreach}` trece în revistă toate postările transmise șablonului în variabila `$posts` și afișează o bucată de cod HTML pentru fiecare postare. La fel cum ar face-o un cod PHP.

Chestia `|date` se numește filtru. Filtrele sunt folosite pentru a formata ieșirea. Acest filtru particular convertește o dată (de exemplu, `2013-04-12`) în forma sa mai ușor de citit (`12. 4. 2013`). Filtrul `|truncate` trunchiază șirul la lungimea maximă specificată și adaugă o elipsă la sfârșit dacă șirul este trunchiat. Deoarece aceasta este o previzualizare, nu are rost să afișeze conținutul complet al articolului. Alte filtre implicite pot [fi găsite în documentație |latte:filters] sau vă puteți crea propriile filtre, dacă este necesar.

Încă un lucru. Putem face codul un pic mai scurt și, astfel, mai simplu. Putem înlocui *Latte tags* cu *n:attributes* astfel:

```latte .{file:app/UI/Home/default.latte}
{block content}
	<h1>My blog</h1>

	<div n:foreach="$posts as $post" class="post">
		<div class="date">{$post->created_at|date:'F j, Y'}</div>

		<h2>{$post->title}</h2>

		<div>{$post->content}</div>
	</div>
{/block}
```

 `n:foreach`, înfășoară pur și simplu *div* cu un bloc *foreach* (face exact același lucru ca și blocul de cod anterior).


Rezumat .[#toc-summary]
=======================

Avem o bază de date MySQL foarte simplă cu câteva articole de blog în ea. Aplicația se conectează la baza de date și afișează o listă simplă a postărilor.

{{priority: -1}}
{{sitename: Nette Quickstart}}

Pagina principală a blogului

Să creăm pagina de pornire care să afișeze postările recente.

Înainte de a începe, ar trebui să știți cel puțin câteva noțiuni de bază despre modelul de proiectare Model-View-Presenter (similar cu MVC):

  • Model – strat de manipulare a datelor. Acesta este complet separat de restul aplicației. Comunică doar cu prezentatorii.
  • View – un strat de definire frontală. Acesta redă datele solicitate utilizatorului folosind șabloane.
  • Prezentator (sau controler) – un nivel de conexiune. Prezentatorul conectează Modelul și View. Gestionează solicitările, cere date de la Model și apoi le transmite la View curent.

În cazul unei aplicații foarte simple precum blogul nostru, stratul Model va consta de fapt doar în interogări către baza de date propriu-zisă – nu avem nevoie de cod PHP suplimentar pentru aceasta. Trebuie doar să creăm straturile Presenter și View. În Nette, fiecare Presenter are propriile sale Views, așa că vom continua cu ambele simultan.

Crearea bazei de date cu Adminer

Pentru a stoca datele, vom folosi baza de date MySQL, deoarece este cea mai frecventă alegere printre dezvoltatorii web. Dar dacă nu vă place, nu ezitați să folosiți o bază de date la alegere.

Să pregătim baza de date care va stoca postările blogului nostru. Putem începe foarte simplu – doar cu un singur tabel pentru postări.

Pentru a crea baza de date putem descărca Adminer, sau puteți folosi un alt instrument pentru gestionarea bazelor de date.

Să deschidem Adminer și să creăm o nouă bază de date numită quickstart.

Creăm un tabel nou numit posts și adăugăm aceste coloane:

  • id int, faceți clic pe autoincrement (AI)
  • title varchar, lungime 255
  • content text
  • created_at timestamp

Ar trebui să arate astfel:

CREATE TABLE `posts` (
	`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`title` varchar(255) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8;

Este foarte important să folosiți stocarea tabelului InnoDB. Veți vedea motivul mai târziu. Deocamdată, alegeți acest lucru și trimiteți. Puteți apăsa Save (Salvare) acum.

Încercați să adăugați câteva exemple de postări pe blog înainte de a implementa capacitatea de a adăuga noi postări direct din aplicația noastră.

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Article One',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Article Two',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Article Three',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);

Conectarea la baza de date

Acum, când baza de date este creată și avem câteva mesaje în ea, este momentul potrivit pentru a le afișa pe noua noastră pagină strălucitoare.

În primul rând, trebuie să spunem aplicației noastre ce bază de date să folosească. Configurația conexiunii la baza de date este stocată în config/common.neon. Setați conexiunea DSN și acreditările. Ar trebui să arate astfel:

database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *enter user name*
	password: *enter password here*

Fiți atenți la indentare în timpul editării acestui fișier. Formatul NEON acceptă atât spații, cât și tabulări, dar nu ambele împreună! Fișierul de configurare din proiectul web utilizează tabulatoarele în mod implicit.

Injectarea conexiunii la baza de date

Prezentatorul HomePresenter, care va lista articolele, are nevoie de o conexiune la baza de date. Pentru a o primi, scrieți un constructor ca acesta:

<?php
namespace App\UI\Home;

use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	// ...
}

Încărcarea mesajelor din baza de date

Acum să preluăm posturile din baza de date și să le transmitem șablonului, care va reda apoi codul HTML. Pentru aceasta există așa-numita metodă render:

public function renderDefault(): void
{
	$this->template->posts = $this->database
		->table('posts')
		->order('created_at DESC')
		->limit(5);
}

Prezentatorul are acum o metodă de randare renderDefault() care transmite datele către o vizualizare numită default. Șabloanele prezentatorului pot fi găsite în app/UI/{PresenterName}/{viewName}.latte, astfel încât în acest caz șablonul va fi localizat în app/UI/Home/default.latte. În șablon, o variabilă numită $posts este acum disponibilă, care conține postările din baza de date.

Șablonul

Există un șablon generic pentru întreaga pagină (numit layout, cu antet, foi de stil, footer, …) și apoi șabloane specifice pentru fiecare vizualizare (de exemplu, pentru afișarea listei de articole de pe blog), care pot suprascrie unele dintre părțile șablonului layout.

În mod implicit, șablonul de aspect este localizat în app/UI/@layout.latte, care conține:

...
{include content}
...

{include content} inserează un bloc numit content în șablonul principal. Îl puteți defini în șabloanele fiecărei vizualizări. În acest caz, vom edita fișierul Home/default.latte în felul următor:

{block content}
	Hello World
{/block}

Acesta definește bloculcontent, care va fi inserat în aspect. Dacă reîmprospătați browserul, veți vedea o pagină cu textul „Hello world“ (în codul sursă, de asemenea, cu antetul și subsolul HTML definite în @layout.latte).

Să afișăm articolele de pe blog – vom edita șablonul astfel:

{block content}
	<h1>My blog</h1>

	{foreach $posts as $post}
	<div class="post">
		<div class="date">{$post->created_at|date:'j. n. Y'}</div>

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
	{/foreach}
{/block}

Dacă vă reîmprospătați browserul, veți vedea lista postărilor de pe blog. Lista nu este foarte fantezistă sau colorată, așa că nu ezitați să adăugați niște CSS strălucitor la www/css/style.css și să o legați într-un layout:

	...
	<link rel="stylesheet" href="{$basePath}/css/style.css">
</head>
...

Eticheta {foreach} trece în revistă toate postările transmise șablonului în variabila $posts și afișează o bucată de cod HTML pentru fiecare postare. La fel cum ar face-o un cod PHP.

Chestia |date se numește filtru. Filtrele sunt folosite pentru a formata ieșirea. Acest filtru particular convertește o dată (de exemplu, 2013-04-12) în forma sa mai ușor de citit (12. 4. 2013). Filtrul |truncate trunchiază șirul la lungimea maximă specificată și adaugă o elipsă la sfârșit dacă șirul este trunchiat. Deoarece aceasta este o previzualizare, nu are rost să afișeze conținutul complet al articolului. Alte filtre implicite pot fi găsite în documentație sau vă puteți crea propriile filtre, dacă este necesar.

Încă un lucru. Putem face codul un pic mai scurt și, astfel, mai simplu. Putem înlocui Latte tags cu n:attributes astfel:

{block content}
	<h1>My blog</h1>

	<div n:foreach="$posts as $post" class="post">
		<div class="date">{$post->created_at|date:'F j, Y'}</div>

		<h2>{$post->title}</h2>

		<div>{$post->content}</div>
	</div>
{/block}

n:foreach, înfășoară pur și simplu div cu un bloc foreach (face exact același lucru ca și blocul de cod anterior).

Rezumat

Avem o bază de date MySQL foarte simplă cu câteva articole de blog în ea. Aplicația se conectează la baza de date și afișează o listă simplă a postărilor.