diff --git a/application/cs/@left-menu.texy b/application/cs/@left-menu.texy index f7378fff34..bd056f35be 100644 --- a/application/cs/@left-menu.texy +++ b/application/cs/@left-menu.texy @@ -4,7 +4,7 @@ Aplikace v Nette - [Bootstrap] - [Presentery |presenters] - [Šablony |templates] -- [Moduly |modules] +- [Adresářová struktura |directory-structure] - [Routování |routing] - [Vytváření odkazů URL |creating-links] - [Interaktivní komponenty |components] diff --git a/application/cs/bootstrap.texy b/application/cs/bootstrap.texy index 19369531d6..5cf576a195 100644 --- a/application/cs/bootstrap.texy +++ b/application/cs/bootstrap.texy @@ -66,7 +66,7 @@ class Bootstrap index.php ========= -Prvotní soubor je v případě webových aplikací `index.php`, který se nachází ve veřejném adresáři `www/`. Ten si nechá od třídy Bootstrap inicializovat prostředí a vyrobit DI kontejner. Poté z něj získá službu `Application`, která spustí webovou aplikaci: +Prvotní soubor je v případě webových aplikací `index.php`, který se nachází ve [veřejném adresáři |directory-structure#verejny-adresar-www] `www/`. Ten si nechá od třídy Bootstrap inicializovat prostředí a vyrobit DI kontejner. Poté z něj získá službu `Application`, která spustí webovou aplikaci: ```php $bootstrap = new App\Bootstrap; diff --git a/application/cs/configuration.texy b/application/cs/configuration.texy index 84954e2e59..96aae17dfc 100644 --- a/application/cs/configuration.texy +++ b/application/cs/configuration.texy @@ -44,7 +44,7 @@ Volba `silentLinks` určuje, jak se Nette zachová ve vývojářském režimu, k [Aliasy zjednodušují odkazování |creating-links#aliasy] na často používané presentery. -[Mapování definuje pravidla |modules#mapování], podle kterých se z názvu presenteru odvodí název třídy. +[Mapování definuje pravidla |directory-structure#mapování presenterů], podle kterých se z názvu presenteru odvodí název třídy. Automatická registrace presenterů diff --git a/application/cs/creating-links.texy b/application/cs/creating-links.texy index 25a1f8d240..45f53eb532 100644 --- a/application/cs/creating-links.texy +++ b/application/cs/creating-links.texy @@ -103,7 +103,7 @@ Pokud je cílem akce `default`, můžeme ji vynechat, ale dvojtečka musí zůst úvodní stránka ``` -Odkazy mohou také směřovat do jiných [modulů |modules]. Zde se odkazy rozlišují na relativní do zanořeného submodulu, nebo absolutní. Princip je analogický k cestám na disku, jen místo lomítek jsou dvojtečky. Předpokládejme, že aktuální presenter je součástí modulu `Front`, potom zapíšeme: +Odkazy mohou také směřovat do jiných [modulů |directory-structure#Presentery a šablony]. Zde se odkazy rozlišují na relativní do zanořeného submodulu, nebo absolutní. Princip je analogický k cestám na disku, jen místo lomítek jsou dvojtečky. Předpokládejme, že aktuální presenter je součástí modulu `Front`, potom zapíšeme: ```latte odkaz na Front:Shop:Product:show @@ -281,6 +281,6 @@ Jak vytvářet odkazy s podobným komfortem jako má metoda `link()`, ale bez p LinkGenerátor je služba, kterou si můžete nechat předat přes konstruktor a poté vytvářet odkazy jeho metodou `link()`. -Oproti presenterům je tu rozdíl. LinkGenerator vytváří všechny odkazy rovnou jako absolutní URL. A dále neexistuje žádný "aktuální presenter", takže nelze jako cíl uvést jen název akce `link('default')` nebo uvádět relativní cesty k [modulům |modules]. +Oproti presenterům je tu rozdíl. LinkGenerator vytváří všechny odkazy rovnou jako absolutní URL. A dále neexistuje žádný "aktuální presenter", takže nelze jako cíl uvést jen název akce `link('default')` nebo uvádět relativní cesty k modulům. Neplatné odkazy vždy vyhazují `Nette\Application\UI\InvalidLinkException`. diff --git a/application/cs/directory-structure.texy b/application/cs/directory-structure.texy new file mode 100644 index 0000000000..35dc8d0ba1 --- /dev/null +++ b/application/cs/directory-structure.texy @@ -0,0 +1,525 @@ +Adresářová struktura aplikace +***************************** + +
+ +Jak navrhnout přehlednou a škálovatelnou adresářovou strukturu pro projekty v Nette Framework? Ukážeme si osvědčené postupy, které vám pomohou s organizací kódu. Dozvíte se: + +- jak **logicky rozčlenit** aplikaci do adresářů +- jak strukturu navrhnout tak, aby **dobře škálovala** s růstem projektu +- jaké jsou **možné alternativy** a jejich výhody či nevýhody + +
+ + +Důležité je zmínit, že Nette Framework samotný na žádné konkrétní struktuře nelpí. Je navržen tak, aby se dal snadno přizpůsobit jakýmkoliv potřebám a preferencím. + + +Základní struktura projektu +=========================== + +Přestože Nette Framework nediktuje žádnou pevnou adresářovou strukturu, existuje osvědčené výchozí uspořádání v podobě [Web Project|https://github.com/nette/web-project]: + +/--pre +web-project/ +├── app/ ← adresář s aplikací +├── assets/ ← soubory SCSS, JS, obrázky..., alternativně resources/ +├── bin/ ← skripty pro příkazovou řádku +├── config/ ← konfigurace +├── log/ ← logované chyby +├── temp/ ← dočasné soubory, cache +├── tests/ ← testy +├── vendor/ ← knihovny instalované Composerem +└── www/ ← veřejný adresář (document-root) +\-- + +Tuto strukturu můžete libovolně upravovat podle svých potřeb - složky přejmenovat či přesouvat. Poté stačí pouze upravit relativní cesty k adresářům v souboru `Bootstrap.php` a případně `composer.json`. Nic víc není potřeba, žádná složitá rekonfigurace, žádné změny konstant. Nette disponuje chytrou autodetekcí a automaticky rozpozná umístění aplikace včetně její URL základny. + + +Principy organizace kódu +======================== + +Když poprve prozkoumáváte nový projekt, měli byste se v něm rychle zorientovat. Představte si, že rozkliknete adresář `app/Model/` a uvidíte tuto strukturu: + +/--pre +app/Model/ +├── Services/ +├── Repositories/ +└── Entities/ +\-- + +Z ní vyčtete jen to, že projekt používá nějaké služby, repozitáře a entity. O skutečném účelu aplikace se nedozvíte vůbec nic. + +Podívejme se na jiný přístup - **organizaci podle domén**: + +/--pre +app/Model/ +├── Cart/ +├── Payment/ +├── Order/ +└── Product/ +\-- + +Tady je to jiné - na první pohled je jasné, že jde o e-shop. Už samotné názvy adresářů prozrazují, co aplikace umí - pracuje s platbami, objednávkami a produkty. + +První přístup (organizace podle typu tříd) přináší v praxi řadu problémů: kód, který spolu logicky souvisí, je roztříštěný do různých složek a musíte mezi nimi přeskakovat. Proto budeme organizovat podle domén. + + +Jmenné prostory +--------------- + +Je zvykem, že adresářová struktura koresponduje se jmennými prostory v aplikaci. To znamená, že fyzické umístění souborů odpovídá jejich namespace. Například třída umístěná v `app/Model/Product/ProductRepository.php` by měla mít namespace `App\Model\Product`. Tento princip pomáhá v orientaci v kódu a zjednodušuje autoloading. + + +Jednotné vs množné číslo v názvech +---------------------------------- + +Všimněte si, že u hlavních adresářů aplikace používáme jednotné číslo: `app`, `config`, `log`, `temp`, `www`. Stejně tak i uvnitř aplikace: `Model`, `Core`, `Presentation`. Je to proto, že každý z nich představuje jeden ucelený koncept. + +Podobně třeba `app/Model/Product` reprezentuje vše kolem produktů. Nenazveme to `Products`, protože nejde o složku plnou produktů (to by tam byly soubory `nokia.php`, `samsung.php`). Je to namespace obsahující třídy pro práci s produkty - `ProductRepository.php`, `ProductService.php`. + +Složka `app/Tasks` je v množném čísle proto, že obsahuje sadu samostatných spustitelných skriptů - `CleanupTask.php`, `ImportTask.php`. Každý z nich je samostatnou jednotkou. + +Pro konzistenci doporučujeme používat: +- Jednotné číslo pro namespace reprezentující funkční celek (byť pracující s více entitami) +- Množné číslo pro kolekce samostatných jednotek +- V případě nejistoty nebo pokud nad tím nechcete přemýšlet, zvolte jednotné číslo + + +Veřejný adresář `www/` +====================== + +Tento adresář je jediný přístupný z webu (tzv. document-root). Často se můžete setkat i s názvem `public/` místo `www/` - je to jen otázka konvence a na funkčnost rostlináře to nemá vliv. Adresář obsahuje: +- [Vstupní bod |bootstrap#index.php] aplikace `index.php` +- Soubor `.htaccess` s pravidly pro mod_rewrite (u Apache) +- Statické soubory (CSS, JavaScript, obrázky) +- Uploadované soubory + +Pro správné zabezpečení aplikace je zásadní mít správně [nakonfigurovaný document-root |nette:troubleshooting#Jak změnit či ostranit z URL adresář www]. + +.[note] +Nikdy neumisťujte do tohoto adresáře složku `node_modules/` - obsahuje tisíce souborů, které mohou být spustitelné a neměly by být veřejně dostupné. + + +Aplikační adresář `app/` +======================== + +Toto je hlavní adresář s aplikačním kódem. Základní struktura: + +/--pre +app/ +├── Core/ ← infrastrukturní záležitosti +├── Model/ ← business logika +├── Presentation/ ← presentery a šablony +├── Tasks/ ← příkazové skripty +└── Bootstrap.php ← zaváděcí třída aplikace +\-- + +`Bootstrap.php` je [startovací třída aplikace|bootstrap], který inicializuje prostředí, načítá konfiguraci a vytváří DI kontejner. + +Pojďme se nyní podívat na jednotlivé podadresáře podrobněji. + + +Presentery a šablony +==================== + +Prezentační část aplikace máme v adresáři `app/Presentation`. Alternativou je krátké `app/UI`. Je to místo pro všechny presentery, jejich šablony a případné pomocné třídy. + +Tuto vrstvu organizujeme podle domén. V komplexním projektu, který kombinuje e-shop, blog a API, by struktura vypadala takto: + +/--pre +app/Presentation/ +├── Shop/ ← e-shop frontend +│ ├── Product/ +│ ├── Cart/ +│ └── Order/ +├── Blog/ ← blog +│ ├── Home/ +│ └── Post/ +├── Admin/ ← administrace +│ ├── Dashboard/ +│ └── Products/ +└── Api/ ← API endpointy + └── V1/ +\-- + +Naopak u jednoduchého blogu bychom použili členění: + +/--pre +app/Presentation/ +├── Front/ ← frontend webu +│ ├── Home/ +│ └── Post/ +├── Admin/ ← administrace +│ ├── Dashboard/ +│ └── Posts/ +├── Error/ +└── Export/ ← RSS, sitemapy atd. +\-- + +Složky jako `Home/` nebo `Dashboard/` obsahují presentery a šablony. Složky jako `Front/`, `Admin/` nebo `Api/` nazýváme **moduly**. Technicky jde o běžné adresáře, které slouží k logickému členění aplikace. + +Každá složka s presenterem obsahuje stejně pojmenovaný presenter a jeho šablony. Například složka `Dashboard/` obsahuje: + +/--pre +Dashboard/ +├── DashboardPresenter.php ← presenter +└── default.latte ← šablona +\-- + +Tato adresářová struktura se odráží ve jmenných prostorech tříd. Například `DashboardPresenter` se nachází ve jmenném prostoru `App\Presentation\Admin\Dashboard` (viz [#mapování presenterů]): + +```php +namespace App\Presentation\Admin\Dashboard; + +class DashboardPresenter extends Nette\Application\UI\Presenter +{ + // ... +} +``` + +Na presenter `Dashboard` uvnitř modulu `Admin` odkazujeme v aplikaci pomocí dvojtečkové notace jako na `Admin:Dashboard`. Na jeho akci `default` potom jako na `Admin:Dashboard:default`. V případě zanořených modulů používáme více dvojteček, například `Shop:Order:Detail:default`. + + +Flexibilní vývoj struktury +-------------------------- + +Jednou z velkých výhod této struktury je, jak elegantně se přizpůsobuje rostoucím potřebám projektu. Jako příklad si vezměme část generující XML feedy. Na začátku máme jednoduchou podobu: + +/--pre +Export/ +├── ExportPresenter.php ← jeden presenter pro všechny exporty +├── sitemap.latte ← šablona pro sitemapu +└── feed.latte ← šablona pro RSS feed +\-- + +Časem přibydou další typy feedů a potřebujeme pro ně více logiky... Žádný problém! Složka `Export/` se jednoduše stane modulem: + +/--pre +Export/ +├── Sitemap/ +│ ├── SitemapPresenter.php +│ └── sitemap.latte +└── Feed/ + ├── FeedPresenter.php + ├── zbozi.latte ← feed pro Zboží.cz + └── heureka.latte ← feed pro Heureka.cz +\-- + +Tato transformace je naprosto plynulá - stačí vytvořit nové podsložky, rozdělit do nich kód a aktualizovat odkazy (např. z `Export:feed` na `Export:Feed:zbozi`). Díky tomu můžeme strukturu postupně rozšiřovat podle potřeby, úroveň zanoření není nijak omezena. + +Pokud například v administraci máte mnoho presenterů týkajících se správy objednávek, jako jsou `OrderDetail`, `OrderEdit`, `OrderDispatch` atd., můžete pro lepší organizovanost v tomto místě vytvořit modul (složku) `Order`, ve kterém budou (složky pro) presentery `Detail`, `Edit`, `Dispatch` a další. + + +Umístění šablon +--------------- + +V předchozích ukázkách jsme viděli, že šablony jsou umístěny přímo ve složce s presenterem: + +``` +Dashboard/ +├── DashboardPresenter.php ← presenter +└── default.latte ← šablona +``` + +Toto umístění se v praxi ukazuje jako nejpohodlnější - všechny související soubory máte hned po ruce. + +Pokud preferujete umístit šablony do podsložky `template`, Nette podporuje obě varianty automaticky. Dokonce můžete všechny šablony, pokud to tak preferujete, umístit úplně mimo `Presentation/` složku. Můžete si vymyslet jakoukoliv konvenci - podorobně na stránce [Hledání šablon|templates#Hledání šablon]. + + +Pomocné třídy a komponenty +-------------------------- + +K prezenterům a šablonám často patří i další pomocné soubory. Umístíme je logicky podle jejich působnosti: + +1. **Přímo u presenteru** v případě specifických komponent pro daný presenter: + +/--pre +Product/ +├── ProductPresenter.php +├── ProductGrid.php ← komponenta pro výpis produktů +└── FilterForm.php ← formulář pro filtrování +\-- + +2. **Pro modul** - doporučujeme využít složku `Accessory`, která se umístí přehledně hned na začátku abecedy: + +/--pre +Front/ +├── Accessory/ +│ ├── NavbarControl.php ← komponenty pro frontend +│ └── TemplateFilters.php +├── Product/ +└── Cart/ +\-- + +3. **Pro celou aplikaci** - v `Presentation/Accessory/`: +/--pre +app/Presentation/ +├── Accessory/ +│ ├── LatteExtension.php +│ └── TemplateFilters.php +├── Front/ +└── Admin/ +\-- + +Nebo můžete pomocné třídy jako `LatteExtension.php` nebo `TemplateFilters.php` umístit do infrastrukturní složky `app/Core/Latte/`. A komponenty do `app/Components`. Volba závisí na zvyklostech týmu. + + +Model - srdce aplikace +====================== + +Model obsahuje veškerou business logiku aplikace. Pro jeho organizaci platí opět pravidlo - strukturujeme podle domén: + +/--pre +app/Model/ +├── Payment/ ← vše kolem plateb +│ ├── PaymentFacade.php ← hlavní vstupní bod +│ ├── PaymentRepository.php +│ ├── Payment.php ← entita +├── Order/ ← vše kolem objednávek +│ ├── OrderFacade.php +│ ├── OrderRepository.php +│ ├── Order.php +└── Shipping/ ← vše kolem dopravy +\-- + +V modelu se typicky setkáte s těmito typy tříd: + +**Fasády**: představují hlavní vstupní bod do konkrétní domény v aplikaci. Působí jako orchestrátor, který koordinuje spolupráci mezi různými službami za účelem implementace kompletních use-cases (jako "vytvoř objednávku" nebo "zpracuj platbu"). Pod svojí orchestrační vrstvou fasáda skrývá implementační detaily před zbytkem aplikace, čímž poskytuje čisté rozhraní pro práci s danou doménou. + +```php +class OrderFacade +{ + public function createOrder(Cart $cart): Order + { + // validace + // vytvoření objednávky + // odeslání e-mailu + // zapsání do statistik + } +} +``` + +**Služby**: zaměřují se na specifickou business operaci v rámci domény. Na rozdíl od fasády, která orchestruje celé use-cases, služba implementuje konkrétní byznys logiku (jako výpočty cen nebo zpracování plateb). Služby jsou typicky bezstavové a mohou být použity buď fasádami jako stavební bloky pro komplexnější operace, nebo přímo jinými částmi aplikace pro jednodušší úkony. + +```php +class PricingService +{ + public function calculateTotal(Order $order): Money + { + // výpočet ceny + } +} +``` + +**Repozitáře**: zajišťují veškerou komunikaci s datovým úložištěm, typicky databází. Jeho úkolem je načítání a ukládání entit a implementace metod pro jejich vyhledávání. Repozitář odstiňuje zbytek aplikace od implementačních detailů databáze a poskytuje objektově orientované rozhraní pro práci s daty. + +```php +class OrderRepository +{ + public function find(int $id): ?Order + { + } + + public function findByCustomer(int $customerId): array + { + } +} +``` + +**Entity**: objekty reprezentující hlavní byznys koncepty v aplikaci, které mají svou identitu a mění se v čase. Typicky jde o třídy mapované na databázové tabulky pomocí ORM (jako Nette Database Explorer nebo Doctrine). Entity mohou obsahovat business pravidla týkající se jejich dat a validační logiku. + +```php +// Entita mapovaná na databázovou tabulku orders +class Order extends Nette\Database\Table\ActiveRow +{ + public function addItem(Product $product, int $quantity): void + { + $this->related('order_items')->insert([ + 'product_id' => $product->id, + 'quantity' => $quantity, + 'unit_price' => $product->price, + ]); + } +} +``` + +**Value objekty**: neměnné objekty reprezentující hodnoty bez vlastní identity - například peněžní částka nebo e-mailová adresa. Dvě instance value objektu se stejnými hodnotami jsou považovány za identické. + + +Infrastrukturní kód +=================== + +Složka `Core/` (nebo také `Infrastructure/`) je domovem pro technický základ aplikace. Infrastrukturní kód typicky zahrnuje: + +/--pre +app/Core/ +├── Router/ ← routování a URL management +│ └── RouterFactory.php +├── Security/ ← autentizace a autorizace +│ ├── Authenticator.php +│ └── Authorizator.php +├── Logging/ ← logování a monitoring +│ ├── SentryLogger.php +│ └── FileLogger.php +├── Cache/ ← cachovací vrstva +│ └── FullPageCache.php +└── Integration/ ← integrace s ext. službami + ├── Slack/ + └── Stripe/ +\-- + +U menších projektů pochopitelně stačí ploché členění: + +/--pre +Core/ +├── RouterFactory.php +├── Authenticator.php +└── QueueMailer.php +\-- + +Jde o kód, který: + +- Řeší technickou infrastrukturu (routování, logování, cachování) +- Integruje externí služby (Sentry, Elasticsearch, Redis) +- Poskytuje základní služby pro celou aplikaci (mail, databáze) +- Je většinou nezávislý na konkrétní doméně - cache nebo logger funguje stejně pro eshop či blog. + +Často tápeme, jestli určitá třída patří sem, nebo do modelu. Klíčový rozdíl je v tom, že kód v `Core/`: + +- Neví nic o doméně (produkty, objednávky, články) +- Je většinou možné ho přenést do jiného projektu +- Řeší "jak to funguje" (jak poslat mail), nikoliv "co to dělá" (jaký mail poslat) + +Příklad pro lepší pochopení: + +- `App\Core\MailerFactory` - vytváří instance třídy pro odesílání e-mailů, řeší SMTP nastavení +- `App\Model\OrderMailer` - používá `MailerFactory` k odesílání e-mailů o objednávkách, zná jejich šablony a ví, kdy se mají poslat + + +Příkazové skripty +================= + +Aplikace často potřebují vykonávat činnosti mimo běžné HTTP požadavky - ať už jde o zpracování dat v pozadí, údržbu, nebo periodické úlohy. Pro spouštění slouží jednoduché skripty v adresáři `bin/`, samotnou implementační logiku pak umisťujeme do `app/Tasks/` (případně `app/Commands/`). + +Rozdělení může být následující: + +/--pre +app/Tasks/ +├── Maintenance/ ← údržbové skripty +│ ├── CleanupCommand.php ← mazání starých dat +│ └── DbOptimizeCommand.php ← optimalizace databáze +├── Integration/ ← integrace s externími systémy +│ ├── ImportProducts.php ← import z dodavatelského systému +│ └── SyncOrders.php ← synchronizace objednávek +└── Scheduled/ ← pravidelné úlohy + ├── NewsletterCommand.php ← rozesílání newsletterů + └── ReminderCommand.php ← notifikace zákazníkům +\-- + +Co patří do modelu a co do příkazových skriptů? Například logika pro odeslání jednoho e-mailu je součástí modelu (`Model/Mail/`), hromadná rozesílka tisíců e-mailů už patří do `Tasks/`. + +Úlohy obvykle [spouštíme z příkazového řádku |https://blog.nette.org/cs/cli-skripty-v-nette-aplikaci] nebo přes cron. Lze je spouštět i přes HTTP požadavek, ale je nutné myslet na bezpečnost. Presenter, který úlohu spustí, je potřeba zabezpečit, například jen pro přihlášené uživatele nebo silným tokenem a přístupem z povolených IP adres. U dlouhých úloh je nutné zvýšit časový limit skriptu a použít `session_write_close()`, aby se nezamykala session. + + +Další možné adresáře +==================== + +Kromě zmíněných základních adresářů můžete podle potřeb projektu přidat další specializované složky. Podívejme se na nejčastější z nich a jejich použití: + +/--pre +app/ +├── Api/ ← logika pro API nezávislá na prezentační vrstvě +├── Database/ ← migrační skripty a seedery pro testovací data +├── Components/ ← sdílené vizuální komponenty napříč celou aplikací +├── Event/ ← užitečné pokud používáte event-driven architekturu +├── Mail/ ← e-mailové šablony a související logika +└── Utils/ ← pomocné třídy +\-- + +Pro sdílené vizuální komponenty používané v presenterech napříč aplikací lze použít složku `app/Components` nebo `app/Controls`: + +/--pre +app/Components/ +├── Form/ ← sdílené formulářové komponenty +│ ├── SignInForm.php +│ └── UserForm.php +├── Grid/ ← komponenty pro výpisy dat +│ └── DataGrid.php +└── Navigation/ ← navigační prvky + ├── Breadcrumbs.php + └── Menu.php +\-- + +Sem patří komponenty, které mají komplexnější logiku. Pokud chcete komponenty sdílet mezi více projekty, je vhodné je vyčlenit do samostatného composer balíčku. + +Do adresáře `app/Mail` můžete umístit správu e-mailové komunikace: + +/--pre +app/Mail/ +├── templates/ ← e-mailové šablony +│ ├── order-confirmation.latte +│ └── welcome.latte +└── OrderMailer.php +\-- + + +Mapování presenterů +=================== + +Mapování definuje pravidla pro odvozování názvu třídy z názvu presenteru. Specifikujeme je v [konfiguraci|configuration] pod klíčem `application › mapping`. + +Na této stránce jsme si ukázali, že presentery umísťujeme do složky `app/Presentation` (případně `app/UI`). Tuto konvenci musíme Nette sdělit v konfiguračním souboru. Stačí jeden řádek: + +```neon +application: + mapping: App\Presentation\*\**Presenter +``` + +Jak mapování funguje? Pro lepší pochopení si nejprve představme aplikaci bez modulů. Chceme, aby třídy presenterů spadaly do jmenného prostoru `App\Presentation`, aby se presenter `Home` mapoval na třídu `App\Presentation\HomePresenter`. Což dosáhneme touto konfigurací: + +```neon +application: + mapping: App\Presentation\*Presenter +``` + +Mapování funguje tak, že název presenteru `Home` nahradí hvězdičku v masce `App\Presentation\*Presenter`, čímž získáme výsledný název třídy `App\Presentation\HomePresenter`. Jednoduché! + +Jak ale vidíte v ukázkách v této a dalších kapitolách, třídy presenterů umisťujeme do eponymních podadresářů, například presenter `Home` se mapuje na třídu `App\Presentation\Home\HomePresenter`. Toho dosáhneme zdvojením dvojtečky (vyžaduje Nette Application 3.2): + +```neon +application: + mapping: App\Presentation\**Presenter +``` + +Nyní přistoupíme k mapování presenterů do modulů. Pro každý modul můžeme definovat specifické mapování: + +```neon +application: + mapping: + Front: App\Presentation\Front\**Presenter + Admin: App\Presentation\Admin\**Presenter + Api: App\Api\*Presenter +``` + +Podle této konfigurace se presenter `Front:Home` mapuje na třídu `App\Presentation\Front\Home\HomePresenter`, zatímco presenter `Api:OAuth` na třídu `App\Api\OAuthPresenter`. + +Protože moduly `Front` i `Admin` mají podobný způsob mapování a takových modulů bude nejspíš více, je možné vytvořit obecné pravidlo, které je nahradí. Do masky třídy tak přibude nová hvězdička pro modul: + +```neon +application: + mapping: + *: App\Presentation\*\**Presenter + Api: App\Api\*Presenter +``` + +Funguje to i pro hlouběji zanořené adresářové struktury, jako je například presenter `Admin:User:Edit`, se segment s hvězdičkou opakuje pro každou úroveň a výsledkem je třída `App\Presentation\Admin\User\Edit\EditPresenter`. + +Alternativním zápisem je místo řetězce použít pole skládající se ze tří segmentů. Tento zápis je ekvivaletní s předchozím: + +```neon +application: + mapping: + *: [App\Presentation, *, **Presenter] + Api: [App\Api, '', *Presenter] +``` diff --git a/application/cs/how-it-works.texy b/application/cs/how-it-works.texy index d58cd6c833..f8944de29e 100644 --- a/application/cs/how-it-works.texy +++ b/application/cs/how-it-works.texy @@ -45,9 +45,9 @@ Adresářová struktura vypadá nějak takto: └── .htaccess ← zakazuje přístup do všech adresářů krom www \-- -Adresářovou strukturu můžete jakkoliv měnit, složky přejmenovat či přesunout, a poté pouze upravit cesty k `log/` a `temp/` v souboru `Bootstrap.php` a dále cestu k tomuto souboru v `composer.json` v sekci `autoload`. Nic víc, žádná složitá rekonfigurace, žádné změny konstant. Nette totiž disponuje [chytrou autodetekcí|bootstrap#vyvojarsky-vs-produkcni-rezim]. +Adresářovou strukturu můžete jakkoliv měnit, složky přejmenovat či přesunout, je zcela flexibilní. Nette navíc disponuje chytrou autodetekcí a automaticky rozpozná umístění aplikace včetně její URL základny. -U trošku větších aplikací můžeme složky s presentery a šablonami rozčlenit na disku do podadresářů a třídy do jmenných prostorů, kterým říkáme [moduly |modules]. +U trošku větších aplikací můžeme složky s presentery a šablonami [rozčlenit do podadresářů |directory-structure#Presentery a šablony] a třídy do jmenných prostorů, kterým říkáme moduly. Adresář `www/` představuje tzv. veřejný adresář neboli document-root projektu. Můžete jej přejmenovat bez nutnosti cokoliv dalšího nastavovat na straně aplikace. Jen je potřeba [nakonfigurovat hosting |nette:troubleshooting#Jak změnit či ostranit z URL adresář www] tak, aby document-root mířil do tohoto adresáře. @@ -75,7 +75,7 @@ Jeho úkolem je: Jakou že továrnu? Nevyrábíme přece traktory, ale webové stránky! Vydržte, hned se to vysvětlí. -Slovy „inicializace prostředí“ myslíme například to, že se aktivuje [Tracy|tracy:], což je úžasný nástroj pro logování nebo vizualizaci chyb. Na produkčním serveru chyby loguje, na vývojovém rovnou zobrazuje. Tudíž k inicializaci patří i rozhodnutí, zda web běží v produkčním nebo vývojářském režimu. K tomu Nette používá autodetekci: pokud web spouštíte na localhost, běží v režimu vývojářském. Nemusíte tak nic konfigurovat a aplikace je rovnou připravena jak pro vývoj, tak ostré nasazení. Tyhle kroky se provádějí a jsou podrobně rozepsané v kapitole o [třídě Bootstrap|bootstrap]. +Slovy „inicializace prostředí“ myslíme například to, že se aktivuje [Tracy|tracy:], což je úžasný nástroj pro logování nebo vizualizaci chyb. Na produkčním serveru chyby loguje, na vývojovém rovnou zobrazuje. Tudíž k inicializaci patří i rozhodnutí, zda web běží v produkčním nebo vývojářském režimu. K tomu Nette používá [chytrou autodetekci|bootstrap#vyvojarsky-vs-produkcni-rezim]: pokud web spouštíte na localhost, běží v režimu vývojářském. Nemusíte tak nic konfigurovat a aplikace je rovnou připravena jak pro vývoj, tak ostré nasazení. Tyhle kroky se provádějí a jsou podrobně rozepsané v kapitole o [třídě Bootstrap|bootstrap]. Třetím bodem (ano, druhý jsme přeskočili, ale vrátíme se k němu) je spuštění aplikace. Vyřizování HTTP požadavků má v Nette na starosti třída `Nette\Application\Application` (dále `Application`), takže když říkáme spustit aplikaci, myslíme tím konkrétně zavolání metody s příznačným názvem `run()` na objektu této třídy. diff --git a/application/cs/modules.texy b/application/cs/modules.texy index f215baec63..b50098649d 100644 --- a/application/cs/modules.texy +++ b/application/cs/modules.texy @@ -1,183 +1 @@ -Moduly -****** - -.[perex] -Moduly vnášejí do Nette aplikací přehlednost díky snadnému členění do logických celků. - -Podobně jako na pevném disku organizujeme soubory do jednotlivých složek, tak i v Nette můžeme presentery, šablony a další pomocné třídy rozdělovat do modulů. Jak to funguje v praxi? Jednoduše začleníme do struktury nové podadresáře. Příklad takové struktury se dvěma moduly Front a Admin: - -/--pre -app/ -├── UI/ -│ ├── Admin/ ← modul Admin -│ │ ├── @layout.latte -│ │ ├── Dashboard/ -│ │ │ ├── DashboardPresenter.php -│ │ │ └── default.latte -│ │ └── ... -│ ├── Front/ ← modul Front -│ │ ├── @layout.latte -│ │ ├── Home/ -│ │ │ ├── HomePresenter.php -│ │ │ └── default.latte -│ │ └── ... -\-- - -Tato adresářová struktura se odráží ve jmenných prostorech tříd, takže například `DashboardPresenter` se nachází ve jmenném prostoru `App\UI\Admin\Dashboard`: - -```php -namespace App\UI\Admin\Dashboard; - -class DashboardPresenter extends Nette\Application\UI\Presenter -{ - // ... -} -``` - -Na presenter `Dashboard` uvnitř modulu `Admin` odkazujeme v aplikaci pomocí dvojtečkové notace jako na `Admin:Dashboard`. Na jeho akci `default` potom jako na `Admin:Dashboard:default`. - -Představená struktura není pevná; můžete si ji zcela [přizpůsobit dle svých potřeb|#mapování] v konfiguraci. .[tip] - -Moduly mohou kromě presenterů a šablon samozřejmě zahrnovat všechny ostatní soubory, jako jsou například komponenty a pomocné třídy. Pokud uvažujete, kam je zařadit, zvažte využití složky `Accessory`: - -/--pre -app/ -├── UI/ -│ ├── Admin/ -│ │ ├── Accessory/ -│ │ │ ├── FormFactory.php -│ │ │ └── AdminLayout.php -│ │ ├── Dashboard/ -│ │ └── ... -\-- - - -Vnořené moduly --------------- - -Moduly mohou mít více úrovní zanoření, podobně jako adresářová struktura na disku: - -/--pre -app/ -├── UI/ -│ ├── Blog/ ← modul Blog -│ │ ├── Admin/ ← submodul Admin -│ │ │ ├── Dashboard/ -│ │ │ └── ... -│ │ ├── Front/ ← submodul Front -│ │ │ ├── @layout.latte -│ │ │ ├── Home/ -│ │ │ └── ... -│ ├── Forum/ ← modul Forum -│ │ └── ... -\-- - -Modul `Blog` je rozdělen na submoduly `Admin` a `Front`. To se projeví i ve jmenných prostorech, které pak budou vypadat jako `App\UI\Blog\Admin` a podobně. Na presenter `Dashboard` v rámci submodulu odkazujeme jako na `Blog:Admin:Dashboard`. - -Zanoření může být libovolně hluboké, což umožňuje vytvářet sub-submoduly. - -Pokud například v administraci máte mnoho presenterů týkajících se správy objednávek, jako jsou `OrderDetail`, `OrderEdit`, `OrderDispatch` atd., můžete pro lepší organizovanost vytvořit modul `Order`, ve kterém budou presentery `Detail`, `Edit`, `Dispatch` a další. - - -Vytváření odkazů ----------------- - -Odkazy v šablonách presenterů jsou relativní vůči aktuálnímu modulu. Tedy odkaz `Foo:default` vede na presenter `Foo` v tomtéž modulu, v jakém je aktuální presenter. Pokud je aktuální modul například `Front`, pak odkaz vede takto: - -```latte -odkaz na Front:Product:show -``` - -Odkaz je relativní i pokud je jeho součástí název modulu, ten se pak považuje za submodul: - -```latte -odkaz na Front:Shop:Product:show -``` - -Absolutní odkazy zapisujeme analogicky k absolutním cestám na disku, jen místo lomítek jsou dvojtečky. Tedy absolutní odkaz začíná dvojtečkou: - -```latte -odkaz na Admin:Product:show -``` - -Pro zjištění, zda jsme v určitém modulu nebo jeho submodulu, použijeme funkci `isModuleCurrent(moduleName)`. - -```latte -
  • - ... -
  • -``` - - -Routování ---------- - -Viz [kapitola o routování |routing#Moduly]. - - -Mapování --------- - -Mapování definuje pravidla pro odvozování názvu třídy z názvu presenteru. Specifikujeme je v [konfiguraci|configuration] pod klíčem `application › mapping`. - -Adresářové struktury uváděné výše na této stránce vycházejí z tohoto mapování: - -```neon -application: - mapping: App\UI\*\**Presenter -``` - -Jak mapování funguje? Pro lepší pochopení si nejprve představme aplikaci bez modulů. Chceme, aby třídy presenterů spadaly do jmenného prostoru `App\UI`, aby se presenter `Home` mapoval na třídu `App\UI\HomePresenter`. Což dosáhneme touto konfigurací: - -```neon -application: - mapping: App\UI\*Presenter -``` - -Mapování funguje tak, že název presenteru `Home` nahradí hvězdičku v masce `App\UI\*Presenter`, čímž získáme výsledný název třídy `App\UI\HomePresenter`. Jednoduché! - -Jak ale vidíte v ukázkách v této a dalších kapitolách, třídy presenterů umisťujeme do eponymních podadresářů, například presenter `Home` se mapuje na třídu `App\UI\Home\HomePresenter`. Toho dosáhneme zdvojením dvojtečky (vyžaduje Nette Application 3.2): - -```neon -application: - mapping: App\UI\**Presenter -``` - -Nyní přistoupíme k mapování presenterů do modulů. Pro každý modul můžeme definovat specifické mapování: - -```neon -application: - mapping: - Front: App\UI\Front\**Presenter - Admin: App\UI\Admin\**Presenter - Api: App\Api\*Presenter -``` - -Podle této konfigurace se presenter `Front:Home` mapuje na třídu `App\UI\Front\Home\HomePresenter`, zatímco presenter `Api:OAuth` na třídu `App\Api\OAuthPresenter`. - -Protože moduly `Front` i `Admin` mají podobný způsob mapování a takových modulů bude nejspíš více, je možné vytvořit obecné pravidlo, které je nahradí. Do masky třídy tak přibude nová hvězdička pro modul: - -```neon -application: - mapping: - *: App\UI\*\**Presenter - Api: App\Api\*Presenter -``` - -Pro vícenásobně zanořené moduly, jako je například presenter `Admin:User:Edit`, se segment s hvězdičkou opakuje pro každou úroveň a výsledkem je třída `App\UI\Admin\User\Edit\EditPresenter`. - -Alternativním zápisem je místo řetězce použít pole skládající se ze tří segmentů. Tento zápis je ekvivaletní s předchozím: - -```neon -application: - mapping: - *: [App\UI, *, **Presenter] - Api: [App\Api, '', *Presenter] -``` - -Pokud bychom měli v konfiguraci jen jediné pravidlo, ono obecné, můžeme zkráceně zapsat: - -```neon -application: - mapping: App\UI\*\**Presenter -``` +{{redirect:directory-structure}} diff --git a/application/cs/routing.texy b/application/cs/routing.texy index ac3cc3aa7a..d997388852 100644 --- a/application/cs/routing.texy +++ b/application/cs/routing.texy @@ -368,7 +368,7 @@ $router->addRoute('', function (string $lang) { Moduly ------ -Pokud máme více rout, které spadají do společného [modulu |modules], využijeme `withModule()`: +Pokud máme více rout, které spadají do společného [modulu |directory-structure#Presentery a šablony], využijeme `withModule()`: ```php $router = new RouteList; diff --git a/application/cs/templates.texy b/application/cs/templates.texy index 470c68748b..183c4426cc 100644 --- a/application/cs/templates.texy +++ b/application/cs/templates.texy @@ -101,7 +101,7 @@ app/ └── @layout.latte ← jen pro Home, 2. varianta \-- -Pokud se presenter nachází v [modulu|modules], bude se dohledávat i o další adresářové úrovně výš, podle zanoření modulu. +Pokud se presenter nachází v modulu, bude se dohledávat i o další adresářové úrovně výš, podle zanoření modulu. Název layoutu lze změnit pomocí `$this->setLayout('layoutAdmin')` a pak se bude očekávat v souboru `@layoutAdmin.latte`. Také lze přímo určit soubor se šablonou layoutu pomocí `$this->setLayout('/path/to/template.latte')`. diff --git a/nette/cs/@home.texy b/nette/cs/@home.texy index ebbd1359df..2fd942b793 100644 --- a/nette/cs/@home.texy +++ b/nette/cs/@home.texy @@ -37,7 +37,7 @@ Aplikace v Nette - [application:Bootstrap] - [Presentery |application:presenters] - [Šablony |application:templates] -- [Moduly |application:modules] +- [Adresářová struktura |application:directory-structure] - [Routování |application:routing] - [Vytváření odkazů URL |application:creating-links] - [Interaktivní komponenty |application:components] diff --git a/nette/cs/glossary.texy b/nette/cs/glossary.texy index e67f066a22..f528196b84 100644 --- a/nette/cs/glossary.texy +++ b/nette/cs/glossary.texy @@ -107,12 +107,14 @@ Architektura, vycházející z [#Model-View-Controller]. Modul ----- -[Modul |application:modules] představuje v Nette Framework balíček presenterů a šablon, případně i komponent a modelů, které dodávají presenteru data. Je to tedy určitá logická část aplikace. +Modul představuje logickou část aplikace. V typickém uspořádání jde o skupinu presenterů a šablon, které řeší určitou oblast funkcionality. Moduly umísťujeme do [samostatných adresářů |application:directory-structure#Presentery a šablony], jako např. `Front/`, `Admin/` nebo `Shop/`. -Například e-shop může mít tři moduly: -1) katalog produktů s košíkem -2) administrace pro zákazníka -3) administrace pro provozovatele +Například e-shop rozdělíme na: +- Frontend (`Shop/`) pro prohlížení produktů a nákup +- Zákaznickou sekci (`Customer/`) pro správu objednávek +- Administraci (`Admin/`) pro provozovatele + +Technicky jde o běžné adresáře, které ale díky přehlednému členění pomáhají aplikaci škálovat. Presenter `Admin:Product:List` tak bude fyzicky umístěn například v adresáři `app/Presentation/Admin/Product/List/` (viz [mapování presenterů|application:directory-structure#mapování presenterů]). Namespace diff --git a/nette/cs/troubleshooting.texy b/nette/cs/troubleshooting.texy index af3cb4858e..5b92bce395 100644 --- a/nette/cs/troubleshooting.texy +++ b/nette/cs/troubleshooting.texy @@ -83,9 +83,14 @@ Jak změnit či ostranit z URL adresář `www`? ------------------------------------------- Adresář `www/` používaný u ukázkových projektů v Nette představuje tzv. veřejný adresář neboli document-root projektu. Jde o jediný adresář, jehož obsah je přístupný prohlížeči. A obsahuje soubor `index.php`, vstupní bod, který spouští webovou aplikaci napsanou v Nette. -Pro zprovoznění aplikace na hostingu je potřeba, abyste v konfiguraci hostingu nastavili tzv. document-root do tohoto adresáře. Nebo, pokud hosting má pro veřejný adresář předpřipravenou složku s jiným názvem (například `web`, `public_html` atd.), tak `www/` jednoduše přejmenujte. +Pro zprovoznění aplikace na hostingu je potřeba mít správně nakonfigurovaný document-root. Máte dvě možnosti: +1. V konfiguraci hostingu nastavit document-root na tento adresář +2. Pokud má hosting předpřipravenou složku (např. `public_html`), přejmenujte `www/` na tento název -Řešením **naopak není** zamezit přístupu do všech složek kromě `www/` pomocí pravidel v souboru `.htaccess` nebo v routeru. Pokud by hosting neumožňoval nastavit document-root do podadresáře (tj. vytvářet adresáře o úroveň výš nad veřejným adresářem), poohlédněte se po jiném. Šli byste jinak do značného bezpečnostního rizika. Bylo by to jako bydlet v bytě, kde nejdou zavřít vstupní dveře a jsou stále dokořán. +.[warning] +Nikdy se nesnažte řešit zabezpečení jen pomocí `.htaccess` nebo routeru, které by zamezovaly přístup do ostatních složek. + +Pokud by hosting neumožňoval nastavit document-root do podadresáře (tj. vytvářet adresáře o úroveň výš nad veřejným adresářem), poohlédněte se po jiném. Šli byste jinak do značného bezpečnostního rizika. Bylo by to jako bydlet v bytě, kde nejdou zavřít vstupní dveře a jsou stále dokořán. Jak nastavit server pro hezká URL?