Nette Documentation Preview

syntax
Az alkalmazás könyvtárszerkezete
********************************

<div class=perex>

Hogyan tervezzünk világos és skálázható könyvtárstruktúrát a projektek számára a Nette Frameworkben? Megmutatjuk a bevált gyakorlatokat, amelyek segítenek a kódod rendszerezésében. Megtanulhatod:

- hogyan kell **logikailag strukturálni** az alkalmazást könyvtárakra osztva
- hogyan tervezze meg a struktúrát úgy, hogy **jól skálázható** legyen a projekt növekedésével együtt
- melyek a **lehetséges alternatívák** és azok előnyei, illetve hátrányai.

</div>


Fontos megemlíteni, hogy maga a Nette Framework nem ragaszkodik semmilyen konkrét struktúrához. Úgy tervezték, hogy könnyen alkalmazkodjon bármilyen igényhez és preferenciához.


Alapvető projektstruktúra .[#toc-basic-project-structure]
=========================================================

Bár a Nette Framework nem diktál semmilyen rögzített könyvtárszerkezetet, van egy bevált alapértelmezett elrendezés a [Web Project |https://github.com/nette/web-project] formájában:

/--pre
<b>web-project/</b>
├── <b>app/</b>              ← alkalmazás könyvtár
├── <b>assets/</b>           ← SCSS, JS fájlok, képek..., alternatívaként resources/
├── <b>bin/</b>              ← parancssori szkriptek
├── <b>config/</b>           ← konfiguráció
├── <b>log/</b>              ← naplózott hibák
├── <b>temp/</b>             ← ideiglenes fájlok, gyorsítótár
├── <b>tests/</b>            ← tesztek
├── <b>vendor/</b>           ← a Composer által telepített könyvtárak
└── <b>www/</b>              ← nyilvános könyvtár (document-root)
\--

Ezt a struktúrát szabadon módosíthatja igényei szerint - átnevezhet vagy áthelyezhet mappákat. Ezután csak a `Bootstrap.php` és esetleg a `composer.json` könyvtárak relatív elérési útvonalait kell beállítania. Semmi másra nincs szükség, nincs bonyolult átkonfigurálás, nincs állandó változtatás. A Nette intelligens automatikus felismeréssel rendelkezik, és automatikusan felismeri az alkalmazás helyét, beleértve annak URL-bázisát is.


Kódszervezési elvek .[#toc-code-organization-principles]
========================================================

Amikor először fedez fel egy új projektet, gyorsan tájékozódnia kell. Képzelje el, hogy rákattint a `app/Model/` könyvtárra, és ezt a struktúrát látja:

/--pre
<b>app/Model/</b>
├── <b>Services/</b>
├── <b>Repositories/</b>
└── <b>Entities/</b>
\--

Ebből csak annyit tudsz meg, hogy a projekt használ néhány szolgáltatást, tárolót és entitást. Az alkalmazás tényleges céljáról semmit sem fog megtudni.

Nézzünk meg egy másik megközelítést - **szervezés tartományok szerint**:

/--pre
<b>app/Model/</b>
├── <b>Cart/</b>
├── <b>Payment/</b>
├── <b>Order/</b>
└── <b>Product/</b>
\--

Ez más - első pillantásra egyértelmű, hogy ez egy e-kereskedelmi oldal. Már a könyvtárnevek is elárulják, hogy mit tud az alkalmazás - fizetésekkel, megrendelésekkel és termékekkel dolgozik.

Az első megközelítés (osztályok típusa szerinti szervezés) a gyakorlatban számos problémát okoz: a logikailag összefüggő kódok különböző mappákban vannak szétszórva, és ezek között ugrálni kell. Ezért szervezünk tartományok szerint.


Névterek .[#toc-namespaces]
---------------------------

Hagyományos, hogy a könyvtárszerkezet megfelel az alkalmazás névtereinek. Ez azt jelenti, hogy a fájlok fizikai helye megfelel a névterüknek. Például egy osztály, amely a `app/Model/Product/ProductRepository.php` cím alatt található, a `App\Model\Product` névtérben kell, hogy legyen. Ez az elv segíti a kódorientációt és egyszerűsíti az automatikus betöltést.


Egyes szám vs. többes szám a nevekben .[#toc-singular-vs-plural-in-names]
-------------------------------------------------------------------------

Vegyük észre, hogy a fő alkalmazási könyvtárak esetében egyes számot használunk: `app`, `config`, `log`, `temp`, `www`. Ugyanez vonatkozik az alkalmazáson belül is: `Model`, `Core`, `Presentation`. Ez azért van így, mert mindegyik egy egységes fogalmat képvisel.

Hasonlóképpen, a `app/Model/Product` mindent képvisel a termékekről. Nem nevezzük `Products`, mert ez nem egy termékekkel teli mappa (amely olyan fájlokat tartalmazna, mint `iphone.php`, `samsung.php`). Ez egy névtér, amely a termékekkel való munkához szükséges osztályokat tartalmazza - `ProductRepository.php`, `ProductService.php`.

A `app/Tasks` mappa többes számban van, mert önállóan futtatható szkripteket tartalmaz - `CleanupTask.php`, `ImportTask.php`. Ezek mindegyike önálló egységet alkot.

A következetesség érdekében javasoljuk a következőket használni:
- Egyes szám a funkcionális egységet képviselő névterek esetében (még akkor is, ha több egységgel dolgozunk).
- többes szám a független egységek gyűjteményeire
- Bizonytalanság esetén, vagy ha nem akarunk ezen gondolkodni, válasszuk az egyes számot.


Nyilvános címtár `www/` .[#toc-public-directory-www]
====================================================

Ez a könyvtár az egyetlen, amely a világhálóról elérhető (úgynevezett dokumentum-gyökér). Gyakran találkozhatsz a `public/` névvel a `www/` helyett - ez csak konvenció kérdése, és nem befolyásolja a funkcionalitást. A könyvtár a következőket tartalmazza:
- Az alkalmazás [belépési pontja |bootstrap#index.php] `index.php`
- `.htaccess` fájl mod_rewrite szabályokkal (Apache esetén)
- Statikus fájlok (CSS, JavaScript, képek)
- Feltöltött fájlok

A megfelelő alkalmazásbiztonság érdekében elengedhetetlen a megfelelően [konfigurált document-root |nette:troubleshooting#how-to-change-or-remove-www-directory-from-url].

.[note]
Soha ne helyezze a `node_modules/` mappát ebbe a könyvtárba - ez több ezer olyan fájlt tartalmaz, amelyek futtathatóak lehetnek, és nem lehetnek nyilvánosan hozzáférhetők.


Alkalmazási könyvtár `app/` .[#toc-application-directory-app]
=============================================================

Ez a fő könyvtár az alkalmazáskóddal. Alapvető struktúra:

/--pre
<b>app/</b>
├── <b>Core/</b>               ← infrastrukturális kérdések
├── <b>Model/</b>              ← üzleti logika
├── <b>Presentation/</b>       ← prezenterek és sablonok
├── <b>Tasks/</b>              ← parancsszkriptek
└── <b>Bootstrap.php</b>       ← alkalmazás bootstrap osztály
\--

`Bootstrap.php` az [alkalmazás indító osztálya |bootstrap], amely inicializálja a környezetet, betölti a konfigurációt és létrehozza a DI konténert.

Most nézzük meg részletesen az egyes alkönyvtárakat.


Előadók és sablonok .[#toc-presenters-and-templates]
====================================================

Az alkalmazás prezentációs része a `app/Presentation` könyvtárban található. Alternatív megoldás a rövid `app/UI`. Ez a hely az összes prezenter, a sablonjaik és az esetleges segédosztályok helye.

Ezt a réteget tartományok szerint szervezzük. Egy összetett projektben, amely egyesíti az e-kereskedelmet, a blogot és az API-t, a struktúra így nézne ki:

/--pre
<b>app/Presentation/</b>
├── <b>Shop/</b>              ← e-kereskedelmi frontend
│   ├── <b>Product/</b>
│   ├── <b>Cart/</b>
│   └── <b>Order/</b>
├── <b>Blog/</b>              ← blog
│   ├── <b>Home/</b>
│   └── <b>Post/</b>
├── <b>Admin/</b>             ← adminisztráció
│   ├── <b>Dashboard/</b>
│   └── <b>Products/</b>
└── <b>Api/</b>               ← API végpontok
	└── <b>V1/</b>
\--

Ezzel szemben egy egyszerű blog esetében ezt a struktúrát használnánk:

/--pre
<b>app/Presentation/</b>
├── <b>Front/</b>             ← weboldal frontend
│   ├── <b>Home/</b>
│   └── <b>Post/</b>
├── <b>Admin/</b>             ← adminisztráció
│   ├── <b>Dashboard/</b>
│   └── <b>Posts/</b>
├── <b>Error/</b>
└── <b>Export/</b>            ← RSS, sitemaps stb.
\--

A `Home/` vagy a `Dashboard/` mappák tartalmazzák az előadókat és a sablonokat. Az olyan mappák, mint a `Front/`, `Admin/` vagy `Api/` a **modulok**. Technikailag ezek szabályos könyvtárak, amelyek az alkalmazás logikai szervezését szolgálják.

Minden bemutatót tartalmazó mappa tartalmaz egy hasonló nevű bemutatót és annak sablonjait. Például a `Dashboard/` mappa a következőket tartalmazza:

/--pre
<b>Dashboard/</b>
├── <b>DashboardPresenter.php</b>     ← előadó
└── <b>default.latte</b>              ← sablon
\--

Ez a könyvtárszerkezet tükröződik az osztályok névterében. Például a `DashboardPresenter` a `App\Presentation\Admin\Dashboard` névtérben található (lásd [az előadói leképezést |#presenter mapping]):

```php
namespace App\Presentation\Admin\Dashboard;

class DashboardPresenter extends Nette\Application\UI\Presenter
{
	//...
}
```

A `Dashboard` prezenterre a `Admin` modulon belül az alkalmazásban a `Admin:Dashboard` kettőspont jelöléssel -ként hivatkozunk. A `default` akciójára ezután `Admin:Dashboard:default`. Beágyazott modulok esetén több kettőspontot használunk, például `Shop:Order:Detail:default`.


Rugalmas struktúrafejlesztés .[#toc-flexible-structure-development]
-------------------------------------------------------------------

Ennek a struktúrának az egyik nagy előnye, hogy milyen elegánsan alkalmazkodik a növekvő projektigényekhez. Példaként vegyük az XML feedeket generáló részt. Kezdetben van egy egyszerű űrlapunk:

/--pre
<b>Export/</b>
├── <b>ExportPresenter.php</b>   ← egy előadó minden exportra
├── <b>sitemap.latte</b>         ← sablon az oldaltérképhez
└── <b>feed.latte</b>            ← sablon RSS feedhez
\--

Idővel több feed-típus kerül hozzá, és több logikára van szükségünk... Nem probléma! A `Export/` mappából egyszerűen modul lesz:

/--pre
<b>Export/</b>
├── <b>Sitemap/</b>
│   ├── <b>SitemapPresenter.php</b>
│   └── <b>sitemap.latte</b>
└── <b>Feed/</b>
	├── <b>FeedPresenter.php</b>
	├── <b>amazon.latte</b>         ← takarmány az Amazon számára
	└── <b>ebay.latte</b>           ← takarmány az eBay számára
\--

Ez az átalakítás teljesen zökkenőmentes - csak hozzon létre új almappákat, ossza fel a kódot ezekre, és frissítse a hivatkozásokat (pl. `Export:feed` -ról `Export:Feed:amazon`-re ). Ennek köszönhetően a struktúrát fokozatosan, igény szerint bővíthetjük, a beágyazási szintnek semmilyen korlátja nincs.

Például, ha az adminisztrációban sok, a rendeléskezeléssel kapcsolatos bemutató van, mint például `OrderDetail`, `OrderEdit`, `OrderDispatch` stb, akkor a jobb szervezés érdekében létrehozhatunk egy `Order` modult (mappát), amely tartalmazni fogja a `Detail`, `Edit`, `Dispatch` és egyéb bemutatók (mappáit).


Sablon helye .[#toc-template-location]
--------------------------------------

Az előző példákban láttuk, hogy a sablonok közvetlenül az előadó mappájában találhatók:

/--pre
<b>Dashboard/</b>
├── <b>DashboardPresenter.php</b>     ← előadó
├── <b>DashboardTemplate.php</b>      ← opcionális sablon osztály
└── <b>default.latte</b>              ← sablon
\--

A gyakorlatban ez a hely bizonyul a legkényelmesebbnek - minden kapcsolódó fájl kéznél van.

Alternatívaként a sablonokat a `templates/` almappában is elhelyezheti. A Nette mindkét változatot támogatja. A sablonokat akár teljesen a `Presentation/` mappán kívül is elhelyezheti. A sablonok elhelyezési lehetőségeiről mindent megtalál a [Sablonok keresése |templates#Template Lookup] fejezetben.


Segédosztályok és komponensek .[#toc-helper-classes-and-components]
-------------------------------------------------------------------

A prezenterek és sablonok gyakran más segédfájlokat is tartalmaznak. Ezeket logikusan a hatókörüknek megfelelően helyezzük el:

1. ** Közvetlenül a prezenterhez** az adott prezenterhez tartozó speciális komponensek esetén:

/--pre
<b>Product/</b>
├── <b>ProductPresenter.php</b>
├── <b>ProductGrid.php</b>        ← komponens a terméklistához
└── <b>FilterForm.php</b>         ← szűrő űrlap
\--

2. **Modulhoz** - javasoljuk a `Accessory` mappát, amely szépen az ábécé elején található:

/--pre
<b>Front/</b>
├── <b>Accessory/</b>
│   ├── <b>NavbarControl.php</b>    ← komponensek frontendhez
│   └── <b>TemplateFilters.php</b>
├── <b>Product/</b>
└── <b>Cart/</b>
\--

3. **A teljes alkalmazáshoz** - a `Presentation/Accessory/`:
/--pre
<b>app/Presentation/</b>
├── <b>Accessory/</b>
│   ├── <b>LatteExtension.php</b>
│   └── <b>TemplateFilters.php</b>
├── <b>Front/</b>
└── <b>Admin/</b>
\--

Vagy elhelyezhet segédosztályokat, mint például `LatteExtension.php` vagy `TemplateFilters.php` az infrastruktúra mappában `app/Core/Latte/`. A komponenseket pedig a `app/Components`. A választás a csapat konvencióitól függ.


Modell - az alkalmazás szíve .[#toc-model-heart-of-the-application]
===================================================================

A modell tartalmazza az alkalmazás teljes üzleti logikáját. Szervezésére ugyanaz a szabály érvényes - tartományok szerint strukturáljuk:

/--pre
<b>app/Model/</b>
├── <b>Payment/</b>                   ← minden a fizetésekről
│   ├── <b>PaymentFacade.php</b>      ← fő belépési pont
│   ├── <b>PaymentRepository.php</b>
│   ├── <b>Payment.php</b>            ← entitás
├── <b>Order/</b>                     ← minden a megrendelésekről
│   ├── <b>OrderFacade.php</b>
│   ├── <b>OrderRepository.php</b>
│   ├── <b>Order.php</b>
└── <b>Shipping/</b>                  ← minden a szállításról
\--

A modellben jellemzően ilyen típusú osztályokkal találkozunk:

**Arcok**: az alkalmazás egy adott tartományának fő belépési pontját jelentik. Olyan orchestrátorként működnek, amely koordinálja a különböző szolgáltatások közötti együttműködést a teljes használati esetek (mint például a "megrendelés létrehozása" vagy a "fizetés feldolgozása") megvalósítása érdekében. Az orkesztrációs rétegük alatt a homlokzat elrejti a megvalósítás részleteit az alkalmazás többi része elől, így egy tiszta felületet biztosít az adott területtel való munkához.

```php
class OrderFacade
{
	public function createOrder(Cart $cart): Order
	{
		// érvényesítés
		// rendelés létrehozása
		// e-mail küldés
		// statisztikák írása
	}
}
```

**Szolgáltatások**: egy tartományon belüli konkrét üzleti műveletekre összpontosítanak. Ellentétben a teljes felhasználási eseteket hangszerelő homlokzatokkal, egy szolgáltatás konkrét üzleti logikát valósít meg (például árkalkulációkat vagy fizetési folyamatokat). A szolgáltatások jellemzően állapot nélküliek, és vagy a homlokzatok használhatják őket építőelemként a bonyolultabb műveletekhez, vagy közvetlenül az alkalmazás más részei az egyszerűbb feladatokhoz.

```php
class PricingService
{
	public function calculateTotal(Order $order): Money
	{
		// árkalkuláció
	}
}
```

**Repozitóriumok**: kezelik az adattárolóval, jellemzően egy adatbázissal folytatott kommunikációt. Feladatuk az entitások betöltése és mentése, valamint a keresésükhöz szükséges módszerek implementálása. A tároló megvédi az alkalmazás többi részét az adatbázis megvalósításának részleteitől, és objektumorientált felületet biztosít az adatokkal való munkához.

```php
class OrderRepository
{
	public function find(int $id): ?Order
	{
	}

	public function findByCustomer(int $customerId): array
	{
	}
}
```

**Entitások**: az alkalmazás fő üzleti fogalmait reprezentáló objektumok, amelyeknek megvan az identitásuk és idővel változnak. Ezek jellemzően ORM (mint például a Nette Database Explorer vagy a Doctrine) segítségével adatbázis táblákra leképezett osztályok. Az entitások tartalmazhatnak az adataikra és az érvényesítési logikára vonatkozó üzleti szabályokat.

```php
// Az adatbázisban szereplő táblára leképezett entitás rendelések
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 objektumok**: megváltoztathatatlan objektumok, amelyek saját identitás nélküli értékeket képviselnek - például egy pénzösszeget vagy egy e-mail címet. Egy értékobjektum két példánya azonos értékekkel azonosnak tekinthető.


Infrastruktúra kód .[#toc-infrastructure-code]
==============================================

A `Core/` mappa (vagy más néven `Infrastructure/`) ad otthont az alkalmazás technikai alapjának. Az infrastrukturális kód jellemzően a következőket tartalmazza:

/--pre
<b>app/Core/</b>
├── <b>Router/</b>               ← útválasztás és URL-kezelés
│   └── <b>RouterFactory.php</b>
├── <b>Security/</b>             ← hitelesítés és engedélyezés
│   ├── <b>Authenticator.php</b>
│   └── <b>Authorizator.php</b>
├── <b>Logging/</b>              ← naplózás és felügyelet
│   ├── <b>SentryLogger.php</b>
│   └── <b>FileLogger.php
├── <b>Cache/</b>                ← gyorsítótárazási réteg
│   └── <b>FullPageCache.php</b>
└── <b>Integration/</b>          ← integráció külső szolgáltatásokkal
	├── <b>Slack/</b>
	└── <b>Stripe/</b>
\--

Kisebb projektek esetében természetesen elegendő a lapos struktúra:

/--pre
<b>Core/</b>
├── <b>RouterFactory.php</b>
├── <b>Authenticator.php</b>
└── <b>QueueMailer.php</b>
\--

Ez a kód:

- Kezeli a technikai infrastruktúrát (útválasztás, naplózás, gyorsítótár).
- Külső szolgáltatásokat integrál (Sentry, Elasticsearch, Redis).
- Alapvető szolgáltatásokat nyújt az egész alkalmazás számára (levelezés, adatbázis).
- Többnyire független az adott tartománytól - a cache vagy a logger ugyanúgy működik az e-kereskedelemben vagy a blogban.

Kíváncsi, hogy egy bizonyos osztály ide vagy a modellbe tartozik-e? A legfontosabb különbség az, hogy a `Core/`:

- Semmit sem tud a domainről (termékek, rendelések, cikkek).
- Általában átvihető egy másik projektbe
- Azt oldja meg, hogy "hogyan működik" (hogyan kell levelet küldeni), nem pedig azt, hogy "mit csinál" (milyen levelet kell küldeni).

Példa a jobb megértéshez:

- `App\Core\MailerFactory` - létrehozza az e-mail küldő osztály példányait, kezeli az SMTP beállításokat.
- `App\Model\OrderMailer` - a `MailerFactory` -t használja a megrendelésekről szóló e-mailek küldésére, ismeri a sablonjaikat és azt, hogy mikor kell elküldeni őket.


Parancsszkriptek .[#toc-command-scripts]
========================================

Az alkalmazásoknak gyakran kell a szokásos HTTP-kéréseken kívüli feladatokat végrehajtaniuk - legyen szó háttéradatfeldolgozásról, karbantartásról vagy időszakos feladatokról. A `bin/` könyvtárban található egyszerű parancsfájlok a végrehajtásra szolgálnak, míg a tényleges végrehajtási logika a `app/Tasks/` (vagy a `app/Commands/`) könyvtárba kerül.

Példa:

/--pre
<b>app/Tasks/</b>
├── <b>Maintenance/</b>               ← karbantartási szkriptek
│   ├── <b>CleanupCommand.php</b>     ← régi adatok törlése
│   └── <b>DbOptimizeCommand.php</b>  ← adatbázis-optimalizálás
├── <b>Integration/</b>               ← integráció külső rendszerekkel
│   ├── <b>ImportProducts.php</b>     ← importálás szállítói rendszerből
│   └── <b>SyncOrders.php</b>         ← rendelések szinkronizálása
└── <b>Scheduled/</b>                 ← rendszeres feladatok
	├── <b>NewsletterCommand.php</b>  ← hírlevelek küldése
	└── <b>ReminderCommand.php</b>    ← vevői értesítések
\--

Mi tartozik a modellbe és mi a parancsszkriptekbe? Például egy e-mail elküldésének logikája a modell része, több ezer e-mail tömeges elküldése a `Tasks/`.

A feladatokat általában [parancssorból |https://blog.nette.org/en/cli-scripts-in-nette-application] vagy cronon keresztül [futtatjuk |https://blog.nette.org/en/cli-scripts-in-nette-application]. HTTP-kérésen keresztül is futtathatók, de a biztonságot figyelembe kell venni. A feladatot futtató bemutatót biztosítani kell, például csak a bejelentkezett felhasználók számára, vagy erős tokennel és engedélyezett IP-címekről való hozzáféréssel. Hosszú feladatok esetén meg kell növelni a szkript időkorlátját, és a `session_write_close()` címet kell használni a munkamenet lezárásának elkerülése érdekében.


Egyéb lehetséges könyvtárak .[#toc-other-possible-directories]
==============================================================

Az említett alapkönyvtárakon kívül a projekt igényeinek megfelelően más speciális mappákat is hozzáadhat. Nézzük meg a leggyakoribbakat és azok használatát:

/--pre
<b>app/</b>
├── <b>Api/</b>              ← A megjelenítési rétegtől független API logika
├── <b>Database/</b>         ← migrációs szkriptek és seederek a tesztadatokhoz
├── <b>Components/</b>       ← megosztott vizuális komponensek az egész alkalmazásban
├── <b>Event/</b>            ← hasznos, ha eseményvezérelt architektúrát használ
├── <b>Mail/</b>             ← e-mail sablonok és kapcsolódó logika
└── <b>Utils/</b>            ← segédosztályok
\--

A prezenterekben az egész alkalmazásban használt, megosztott vizuális komponensekhez használhatja a `app/Components` vagy a `app/Controls` mappát:

/--pre
<b>app/Components/</b>
├── <b>Form/</b>                 ← megosztott űrlap komponensek
│   ├── <b>SignInForm.php</b>
│   └── <b>UserForm.php</b>
├── <b>Grid/</b>                 ← komponensek adatlistákhoz
│   └── <b>DataGrid.php</b>
└── <b>Navigation/</b>           ← navigációs elemek
	├── <b>Breadcrumbs.php</b>
	└── <b>Menu.php</b>
\--

Ide tartoznak az összetettebb logikájú komponensek. Ha a komponenseket több projekt között szeretné megosztani, akkor érdemes azokat egy önálló composer csomagban elkülöníteni.

A `app/Mail` könyvtárban helyezheti el az e-mail kommunikáció kezelését:

/--pre
<b>app/Mail/</b>
├── <b>templates/</b>            ← e-mail sablonok
│   ├── <b>order-confirmation.latte</b>
│   └── <b>welcome.latte</b>
└── <b>OrderMailer.php</b>
\--


Előadótérképezés .[#toc-presenter-mapping]
==========================================

A leképezés szabályokat határoz meg az osztályok nevének a bemutató nevéből történő származtatására. Ezeket a [konfigurációban |configuration] a `application › mapping` kulcs alatt adjuk meg.

Ezen az oldalon megmutattuk, hogy az előadókat a `app/Presentation` mappába (vagy a `app/UI`) helyezzük el. Erről a konvencióról a konfigurációs fájlban kell tájékoztatnunk a Nette-et. Egyetlen sor elég:

```neon
application:
	mapping: App\Presentation\*\**Presenter
```

Hogyan működik a leképezés? A jobb megértéshez először képzeljünk el egy modulok nélküli alkalmazást. Szeretnénk, ha a prezenter osztályok a `App\Presentation` névtér alá tartoznának, így a `Home` prezenter a `App\Presentation\HomePresenter` osztályhoz tartozik. Ezt a következő konfigurációval érjük el:

```neon
application:
	mapping: App\Presentation\*Presenter
```

A leképezés úgy működik, hogy a `App\Presentation\*Presenter` maszkban a csillagot a `Home` prezenter névvel helyettesítjük, ami a `App\Presentation\HomePresenter` végső osztálynevet eredményezi. Egyszerű!

Azonban, ahogy ebben és más fejezetekben található példákban is láthatjuk, a prezenter osztályokat névadó alkönyvtárakba helyezzük, például a `Home` prezenter a `App\Presentation\Home\HomePresenter` osztályra képezi le a prezentert. Ezt a kettőspont megduplázásával érjük el (Nette Application 3.2 szükséges):

```neon
application:
	mapping: App\Presentation\**Presenter
```

Most áttérünk a prezenterek modulokba való leképezésére. Minden egyes modulhoz sajátos leképezést határozhatunk meg:

```neon
application:
	mapping:
		Front: App\Presentation\Front\**Presenter
		Admin: App\Presentation\Admin\**Presenter
		Api: App\Api\*Presenter
```

E konfiguráció szerint a `Front:Home` bemutató a `App\Presentation\Front\Home\HomePresenter` osztályhoz, míg a `Api:OAuth` bemutató a `App\Api\OAuthPresenter` osztályhoz tartozik.

Mivel a `Front` és a `Admin` modulok hasonló leképezési módszerrel rendelkeznek, és valószínűleg több ilyen modul is lesz, létrehozható egy általános szabály, amely helyettesíti őket. Az osztálymaszkhoz egy új csillagot adunk a modulhoz:

```neon
application:
	mapping:
		*: App\Presentation\*\**Presenter
		Api: App\Api\*Presenter
```

Ez működik a mélyebben egymásba ágyazott könyvtárstruktúrák esetében is, mint például a `Admin:User:Edit` bemutató, ahol a csillaggal ellátott szegmens minden szinten megismétlődik, és a `App\Presentation\Admin\User\Edit\EditPresenter` osztályt eredményezi.

Egy alternatív jelölés az, ha a karakterlánc helyett egy három szegmensből álló tömböt használunk. Ez a jelölés egyenértékű az előzővel:

```neon
application:
	mapping:
		*: [App\Presentation, *, **Presenter]
		Api: [App\Api, '', *Presenter]
```

Az alkalmazás könyvtárszerkezete

Hogyan tervezzünk világos és skálázható könyvtárstruktúrát a projektek számára a Nette Frameworkben? Megmutatjuk a bevált gyakorlatokat, amelyek segítenek a kódod rendszerezésében. Megtanulhatod:

  • hogyan kell logikailag strukturálni az alkalmazást könyvtárakra osztva
  • hogyan tervezze meg a struktúrát úgy, hogy jól skálázható legyen a projekt növekedésével együtt
  • melyek a lehetséges alternatívák és azok előnyei, illetve hátrányai.

Fontos megemlíteni, hogy maga a Nette Framework nem ragaszkodik semmilyen konkrét struktúrához. Úgy tervezték, hogy könnyen alkalmazkodjon bármilyen igényhez és preferenciához.

Alapvető projektstruktúra

Bár a Nette Framework nem diktál semmilyen rögzített könyvtárszerkezetet, van egy bevált alapértelmezett elrendezés a Web Project formájában:

web-project/
├── app/              ← alkalmazás könyvtár
├── assets/           ← SCSS, JS fájlok, képek..., alternatívaként resources/
├── bin/              ← parancssori szkriptek
├── config/           ← konfiguráció
├── log/              ← naplózott hibák
├── temp/             ← ideiglenes fájlok, gyorsítótár
├── tests/            ← tesztek
├── vendor/           ← a Composer által telepített könyvtárak
└── www/              ← nyilvános könyvtár (document-root)

Ezt a struktúrát szabadon módosíthatja igényei szerint – átnevezhet vagy áthelyezhet mappákat. Ezután csak a Bootstrap.php és esetleg a composer.json könyvtárak relatív elérési útvonalait kell beállítania. Semmi másra nincs szükség, nincs bonyolult átkonfigurálás, nincs állandó változtatás. A Nette intelligens automatikus felismeréssel rendelkezik, és automatikusan felismeri az alkalmazás helyét, beleértve annak URL-bázisát is.

Kódszervezési elvek

Amikor először fedez fel egy új projektet, gyorsan tájékozódnia kell. Képzelje el, hogy rákattint a app/Model/ könyvtárra, és ezt a struktúrát látja:

app/Model/
├── Services/
├── Repositories/
└── Entities/

Ebből csak annyit tudsz meg, hogy a projekt használ néhány szolgáltatást, tárolót és entitást. Az alkalmazás tényleges céljáról semmit sem fog megtudni.

Nézzünk meg egy másik megközelítést – szervezés tartományok szerint:

app/Model/
├── Cart/
├── Payment/
├── Order/
└── Product/

Ez más – első pillantásra egyértelmű, hogy ez egy e-kereskedelmi oldal. Már a könyvtárnevek is elárulják, hogy mit tud az alkalmazás – fizetésekkel, megrendelésekkel és termékekkel dolgozik.

Az első megközelítés (osztályok típusa szerinti szervezés) a gyakorlatban számos problémát okoz: a logikailag összefüggő kódok különböző mappákban vannak szétszórva, és ezek között ugrálni kell. Ezért szervezünk tartományok szerint.

Névterek

Hagyományos, hogy a könyvtárszerkezet megfelel az alkalmazás névtereinek. Ez azt jelenti, hogy a fájlok fizikai helye megfelel a névterüknek. Például egy osztály, amely a app/Model/Product/ProductRepository.php cím alatt található, a App\Model\Product névtérben kell, hogy legyen. Ez az elv segíti a kódorientációt és egyszerűsíti az automatikus betöltést.

Egyes szám vs. többes szám a nevekben

Vegyük észre, hogy a fő alkalmazási könyvtárak esetében egyes számot használunk: app, config, log, temp, www. Ugyanez vonatkozik az alkalmazáson belül is: Model, Core, Presentation. Ez azért van így, mert mindegyik egy egységes fogalmat képvisel.

Hasonlóképpen, a app/Model/Product mindent képvisel a termékekről. Nem nevezzük Products, mert ez nem egy termékekkel teli mappa (amely olyan fájlokat tartalmazna, mint iphone.php, samsung.php). Ez egy névtér, amely a termékekkel való munkához szükséges osztályokat tartalmazza – ProductRepository.php, ProductService.php.

A app/Tasks mappa többes számban van, mert önállóan futtatható szkripteket tartalmaz – CleanupTask.php, ImportTask.php. Ezek mindegyike önálló egységet alkot.

A következetesség érdekében javasoljuk a következőket használni:

  • Egyes szám a funkcionális egységet képviselő névterek esetében (még akkor is, ha több egységgel dolgozunk).
  • többes szám a független egységek gyűjteményeire
  • Bizonytalanság esetén, vagy ha nem akarunk ezen gondolkodni, válasszuk az egyes számot.

Nyilvános címtár www/

Ez a könyvtár az egyetlen, amely a világhálóról elérhető (úgynevezett dokumentum-gyökér). Gyakran találkozhatsz a public/ névvel a www/ helyett – ez csak konvenció kérdése, és nem befolyásolja a funkcionalitást. A könyvtár a következőket tartalmazza:

  • Az alkalmazás belépési pontja index.php
  • .htaccess fájl mod_rewrite szabályokkal (Apache esetén)
  • Statikus fájlok (CSS, JavaScript, képek)
  • Feltöltött fájlok

A megfelelő alkalmazásbiztonság érdekében elengedhetetlen a megfelelően konfigurált document-root.

Soha ne helyezze a node_modules/ mappát ebbe a könyvtárba – ez több ezer olyan fájlt tartalmaz, amelyek futtathatóak lehetnek, és nem lehetnek nyilvánosan hozzáférhetők.

Alkalmazási könyvtár app/

Ez a fő könyvtár az alkalmazáskóddal. Alapvető struktúra:

app/
├── Core/               ← infrastrukturális kérdések
├── Model/              ← üzleti logika
├── Presentation/       ← prezenterek és sablonok
├── Tasks/              ← parancsszkriptek
└── Bootstrap.php       ← alkalmazás bootstrap osztály

Bootstrap.php az alkalmazás indító osztálya, amely inicializálja a környezetet, betölti a konfigurációt és létrehozza a DI konténert.

Most nézzük meg részletesen az egyes alkönyvtárakat.

Előadók és sablonok

Az alkalmazás prezentációs része a app/Presentation könyvtárban található. Alternatív megoldás a rövid app/UI. Ez a hely az összes prezenter, a sablonjaik és az esetleges segédosztályok helye.

Ezt a réteget tartományok szerint szervezzük. Egy összetett projektben, amely egyesíti az e-kereskedelmet, a blogot és az API-t, a struktúra így nézne ki:

app/Presentation/
├── Shop/              ← e-kereskedelmi frontend
│   ├── Product/
│   ├── Cart/
│   └── Order/
├── Blog/              ← blog
│   ├── Home/
│   └── Post/
├── Admin/             ← adminisztráció
│   ├── Dashboard/
│   └── Products/
└── Api/               ← API végpontok
	└── V1/

Ezzel szemben egy egyszerű blog esetében ezt a struktúrát használnánk:

app/Presentation/
├── Front/             ← weboldal frontend
│   ├── Home/
│   └── Post/
├── Admin/             ← adminisztráció
│   ├── Dashboard/
│   └── Posts/
├── Error/
└── Export/            ← RSS, sitemaps stb.

A Home/ vagy a Dashboard/ mappák tartalmazzák az előadókat és a sablonokat. Az olyan mappák, mint a Front/, Admin/ vagy Api/ a modulok. Technikailag ezek szabályos könyvtárak, amelyek az alkalmazás logikai szervezését szolgálják.

Minden bemutatót tartalmazó mappa tartalmaz egy hasonló nevű bemutatót és annak sablonjait. Például a Dashboard/ mappa a következőket tartalmazza:

Dashboard/
├── DashboardPresenter.php     ← előadó
└── default.latte              ← sablon

Ez a könyvtárszerkezet tükröződik az osztályok névterében. Például a DashboardPresenter a App\Presentation\Admin\Dashboard névtérben található (lásd az előadói leképezést):

namespace App\Presentation\Admin\Dashboard;

class DashboardPresenter extends Nette\Application\UI\Presenter
{
	//...
}

A Dashboard prezenterre a Admin modulon belül az alkalmazásban a Admin:Dashboard kettőspont jelöléssel -ként hivatkozunk. A default akciójára ezután Admin:Dashboard:default. Beágyazott modulok esetén több kettőspontot használunk, például Shop:Order:Detail:default.

Rugalmas struktúrafejlesztés

Ennek a struktúrának az egyik nagy előnye, hogy milyen elegánsan alkalmazkodik a növekvő projektigényekhez. Példaként vegyük az XML feedeket generáló részt. Kezdetben van egy egyszerű űrlapunk:

Export/
├── ExportPresenter.php   ← egy előadó minden exportra
├── sitemap.latte         ← sablon az oldaltérképhez
└── feed.latte            ← sablon RSS feedhez

Idővel több feed-típus kerül hozzá, és több logikára van szükségünk… Nem probléma! A Export/ mappából egyszerűen modul lesz:

Export/
├── Sitemap/
│   ├── SitemapPresenter.php
│   └── sitemap.latte
└── Feed/
	├── FeedPresenter.php
	├── amazon.latte         ← takarmány az Amazon számára
	└── ebay.latte           ← takarmány az eBay számára

Ez az átalakítás teljesen zökkenőmentes – csak hozzon létre új almappákat, ossza fel a kódot ezekre, és frissítse a hivatkozásokat (pl. Export:feed -ról Export:Feed:amazon-re ). Ennek köszönhetően a struktúrát fokozatosan, igény szerint bővíthetjük, a beágyazási szintnek semmilyen korlátja nincs.

Például, ha az adminisztrációban sok, a rendeléskezeléssel kapcsolatos bemutató van, mint például OrderDetail, OrderEdit, OrderDispatch stb, akkor a jobb szervezés érdekében létrehozhatunk egy Order modult (mappát), amely tartalmazni fogja a Detail, Edit, Dispatch és egyéb bemutatók (mappáit).

Sablon helye

Az előző példákban láttuk, hogy a sablonok közvetlenül az előadó mappájában találhatók:

Dashboard/
├── DashboardPresenter.php     ← előadó
├── DashboardTemplate.php      ← opcionális sablon osztály
└── default.latte              ← sablon

A gyakorlatban ez a hely bizonyul a legkényelmesebbnek – minden kapcsolódó fájl kéznél van.

Alternatívaként a sablonokat a templates/ almappában is elhelyezheti. A Nette mindkét változatot támogatja. A sablonokat akár teljesen a Presentation/ mappán kívül is elhelyezheti. A sablonok elhelyezési lehetőségeiről mindent megtalál a Sablonok keresése fejezetben.

Segédosztályok és komponensek

A prezenterek és sablonok gyakran más segédfájlokat is tartalmaznak. Ezeket logikusan a hatókörüknek megfelelően helyezzük el:

1. ** Közvetlenül a prezenterhez** az adott prezenterhez tartozó speciális komponensek esetén:

Product/
├── ProductPresenter.php
├── ProductGrid.php        ← komponens a terméklistához
└── FilterForm.php         ← szűrő űrlap

2. Modulhoz – javasoljuk a Accessory mappát, amely szépen az ábécé elején található:

Front/
├── Accessory/
│   ├── NavbarControl.php    ← komponensek frontendhez
│   └── TemplateFilters.php
├── Product/
└── Cart/

3. A teljes alkalmazáshoz – a Presentation/Accessory/:

app/Presentation/
├── Accessory/
│   ├── LatteExtension.php
│   └── TemplateFilters.php
├── Front/
└── Admin/

Vagy elhelyezhet segédosztályokat, mint például LatteExtension.php vagy TemplateFilters.php az infrastruktúra mappában app/Core/Latte/. A komponenseket pedig a app/Components. A választás a csapat konvencióitól függ.

Modell – az alkalmazás szíve

A modell tartalmazza az alkalmazás teljes üzleti logikáját. Szervezésére ugyanaz a szabály érvényes – tartományok szerint strukturáljuk:

app/Model/
├── Payment/                   ← minden a fizetésekről
│   ├── PaymentFacade.php      ← fő belépési pont
│   ├── PaymentRepository.php
│   ├── Payment.php            ← entitás
├── Order/                     ← minden a megrendelésekről
│   ├── OrderFacade.php
│   ├── OrderRepository.php
│   ├── Order.php
└── Shipping/                  ← minden a szállításról

A modellben jellemzően ilyen típusú osztályokkal találkozunk:

Arcok: az alkalmazás egy adott tartományának fő belépési pontját jelentik. Olyan orchestrátorként működnek, amely koordinálja a különböző szolgáltatások közötti együttműködést a teljes használati esetek (mint például a „megrendelés létrehozása“ vagy a „fizetés feldolgozása“) megvalósítása érdekében. Az orkesztrációs rétegük alatt a homlokzat elrejti a megvalósítás részleteit az alkalmazás többi része elől, így egy tiszta felületet biztosít az adott területtel való munkához.

class OrderFacade
{
	public function createOrder(Cart $cart): Order
	{
		// érvényesítés
		// rendelés létrehozása
		// e-mail küldés
		// statisztikák írása
	}
}

Szolgáltatások: egy tartományon belüli konkrét üzleti műveletekre összpontosítanak. Ellentétben a teljes felhasználási eseteket hangszerelő homlokzatokkal, egy szolgáltatás konkrét üzleti logikát valósít meg (például árkalkulációkat vagy fizetési folyamatokat). A szolgáltatások jellemzően állapot nélküliek, és vagy a homlokzatok használhatják őket építőelemként a bonyolultabb műveletekhez, vagy közvetlenül az alkalmazás más részei az egyszerűbb feladatokhoz.

class PricingService
{
	public function calculateTotal(Order $order): Money
	{
		// árkalkuláció
	}
}

Repozitóriumok: kezelik az adattárolóval, jellemzően egy adatbázissal folytatott kommunikációt. Feladatuk az entitások betöltése és mentése, valamint a keresésükhöz szükséges módszerek implementálása. A tároló megvédi az alkalmazás többi részét az adatbázis megvalósításának részleteitől, és objektumorientált felületet biztosít az adatokkal való munkához.

class OrderRepository
{
	public function find(int $id): ?Order
	{
	}

	public function findByCustomer(int $customerId): array
	{
	}
}

Entitások: az alkalmazás fő üzleti fogalmait reprezentáló objektumok, amelyeknek megvan az identitásuk és idővel változnak. Ezek jellemzően ORM (mint például a Nette Database Explorer vagy a Doctrine) segítségével adatbázis táblákra leképezett osztályok. Az entitások tartalmazhatnak az adataikra és az érvényesítési logikára vonatkozó üzleti szabályokat.

// Az adatbázisban szereplő táblára leképezett entitás rendelések
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 objektumok: megváltoztathatatlan objektumok, amelyek saját identitás nélküli értékeket képviselnek – például egy pénzösszeget vagy egy e-mail címet. Egy értékobjektum két példánya azonos értékekkel azonosnak tekinthető.

Infrastruktúra kód

A Core/ mappa (vagy más néven Infrastructure/) ad otthont az alkalmazás technikai alapjának. Az infrastrukturális kód jellemzően a következőket tartalmazza:

app/Core/
├── Router/               ← útválasztás és URL-kezelés
│   └── RouterFactory.php
├── Security/             ← hitelesítés és engedélyezés
│   ├── Authenticator.php
│   └── Authorizator.php
├── Logging/              ← naplózás és felügyelet
│   ├── SentryLogger.php
│   └── FileLogger.php
├── Cache/                ← gyorsítótárazási réteg
│   └── FullPageCache.php
└── Integration/          ← integráció külső szolgáltatásokkal
	├── Slack/
	└── Stripe/

Kisebb projektek esetében természetesen elegendő a lapos struktúra:

Core/
├── RouterFactory.php
├── Authenticator.php
└── QueueMailer.php

Ez a kód:

  • Kezeli a technikai infrastruktúrát (útválasztás, naplózás, gyorsítótár).
  • Külső szolgáltatásokat integrál (Sentry, Elasticsearch, Redis).
  • Alapvető szolgáltatásokat nyújt az egész alkalmazás számára (levelezés, adatbázis).
  • Többnyire független az adott tartománytól – a cache vagy a logger ugyanúgy működik az e-kereskedelemben vagy a blogban.

Kíváncsi, hogy egy bizonyos osztály ide vagy a modellbe tartozik-e? A legfontosabb különbség az, hogy a Core/:

  • Semmit sem tud a domainről (termékek, rendelések, cikkek).
  • Általában átvihető egy másik projektbe
  • Azt oldja meg, hogy „hogyan működik“ (hogyan kell levelet küldeni), nem pedig azt, hogy „mit csinál“ (milyen levelet kell küldeni).

Példa a jobb megértéshez:

  • App\Core\MailerFactory – létrehozza az e-mail küldő osztály példányait, kezeli az SMTP beállításokat.
  • App\Model\OrderMailer – a MailerFactory -t használja a megrendelésekről szóló e-mailek küldésére, ismeri a sablonjaikat és azt, hogy mikor kell elküldeni őket.

Parancsszkriptek

Az alkalmazásoknak gyakran kell a szokásos HTTP-kéréseken kívüli feladatokat végrehajtaniuk – legyen szó háttéradatfeldolgozásról, karbantartásról vagy időszakos feladatokról. A bin/ könyvtárban található egyszerű parancsfájlok a végrehajtásra szolgálnak, míg a tényleges végrehajtási logika a app/Tasks/ (vagy a app/Commands/) könyvtárba kerül.

Példa:

app/Tasks/
├── Maintenance/               ← karbantartási szkriptek
│   ├── CleanupCommand.php     ← régi adatok törlése
│   └── DbOptimizeCommand.php  ← adatbázis-optimalizálás
├── Integration/               ← integráció külső rendszerekkel
│   ├── ImportProducts.php     ← importálás szállítói rendszerből
│   └── SyncOrders.php         ← rendelések szinkronizálása
└── Scheduled/                 ← rendszeres feladatok
	├── NewsletterCommand.php  ← hírlevelek küldése
	└── ReminderCommand.php    ← vevői értesítések

Mi tartozik a modellbe és mi a parancsszkriptekbe? Például egy e-mail elküldésének logikája a modell része, több ezer e-mail tömeges elküldése a Tasks/.

A feladatokat általában parancssorból vagy cronon keresztül futtatjuk. HTTP-kérésen keresztül is futtathatók, de a biztonságot figyelembe kell venni. A feladatot futtató bemutatót biztosítani kell, például csak a bejelentkezett felhasználók számára, vagy erős tokennel és engedélyezett IP-címekről való hozzáféréssel. Hosszú feladatok esetén meg kell növelni a szkript időkorlátját, és a session_write_close() címet kell használni a munkamenet lezárásának elkerülése érdekében.

Egyéb lehetséges könyvtárak

Az említett alapkönyvtárakon kívül a projekt igényeinek megfelelően más speciális mappákat is hozzáadhat. Nézzük meg a leggyakoribbakat és azok használatát:

app/
├── Api/              ← A megjelenítési rétegtől független API logika
├── Database/         ← migrációs szkriptek és seederek a tesztadatokhoz
├── Components/       ← megosztott vizuális komponensek az egész alkalmazásban
├── Event/            ← hasznos, ha eseményvezérelt architektúrát használ
├── Mail/             ← e-mail sablonok és kapcsolódó logika
└── Utils/            ← segédosztályok

A prezenterekben az egész alkalmazásban használt, megosztott vizuális komponensekhez használhatja a app/Components vagy a app/Controls mappát:

app/Components/
├── Form/                 ← megosztott űrlap komponensek
│   ├── SignInForm.php
│   └── UserForm.php
├── Grid/                 ← komponensek adatlistákhoz
│   └── DataGrid.php
└── Navigation/           ← navigációs elemek
	├── Breadcrumbs.php
	└── Menu.php

Ide tartoznak az összetettebb logikájú komponensek. Ha a komponenseket több projekt között szeretné megosztani, akkor érdemes azokat egy önálló composer csomagban elkülöníteni.

A app/Mail könyvtárban helyezheti el az e-mail kommunikáció kezelését:

app/Mail/
├── templates/            ← e-mail sablonok
│   ├── order-confirmation.latte
│   └── welcome.latte
└── OrderMailer.php

Előadótérképezés

A leképezés szabályokat határoz meg az osztályok nevének a bemutató nevéből történő származtatására. Ezeket a konfigurációban a application › mapping kulcs alatt adjuk meg.

Ezen az oldalon megmutattuk, hogy az előadókat a app/Presentation mappába (vagy a app/UI) helyezzük el. Erről a konvencióról a konfigurációs fájlban kell tájékoztatnunk a Nette-et. Egyetlen sor elég:

application:
	mapping: App\Presentation\*\**Presenter

Hogyan működik a leképezés? A jobb megértéshez először képzeljünk el egy modulok nélküli alkalmazást. Szeretnénk, ha a prezenter osztályok a App\Presentation névtér alá tartoznának, így a Home prezenter a App\Presentation\HomePresenter osztályhoz tartozik. Ezt a következő konfigurációval érjük el:

application:
	mapping: App\Presentation\*Presenter

A leképezés úgy működik, hogy a App\Presentation\*Presenter maszkban a csillagot a Home prezenter névvel helyettesítjük, ami a App\Presentation\HomePresenter végső osztálynevet eredményezi. Egyszerű!

Azonban, ahogy ebben és más fejezetekben található példákban is láthatjuk, a prezenter osztályokat névadó alkönyvtárakba helyezzük, például a Home prezenter a App\Presentation\Home\HomePresenter osztályra képezi le a prezentert. Ezt a kettőspont megduplázásával érjük el (Nette Application 3.2 szükséges):

application:
	mapping: App\Presentation\**Presenter

Most áttérünk a prezenterek modulokba való leképezésére. Minden egyes modulhoz sajátos leképezést határozhatunk meg:

application:
	mapping:
		Front: App\Presentation\Front\**Presenter
		Admin: App\Presentation\Admin\**Presenter
		Api: App\Api\*Presenter

E konfiguráció szerint a Front:Home bemutató a App\Presentation\Front\Home\HomePresenter osztályhoz, míg a Api:OAuth bemutató a App\Api\OAuthPresenter osztályhoz tartozik.

Mivel a Front és a Admin modulok hasonló leképezési módszerrel rendelkeznek, és valószínűleg több ilyen modul is lesz, létrehozható egy általános szabály, amely helyettesíti őket. Az osztálymaszkhoz egy új csillagot adunk a modulhoz:

application:
	mapping:
		*: App\Presentation\*\**Presenter
		Api: App\Api\*Presenter

Ez működik a mélyebben egymásba ágyazott könyvtárstruktúrák esetében is, mint például a Admin:User:Edit bemutató, ahol a csillaggal ellátott szegmens minden szinten megismétlődik, és a App\Presentation\Admin\User\Edit\EditPresenter osztályt eredményezi.

Egy alternatív jelölés az, ha a karakterlánc helyett egy három szegmensből álló tömböt használunk. Ez a jelölés egyenértékű az előzővel:

application:
	mapping:
		*: [App\Presentation, *, **Presenter]
		Api: [App\Api, '', *Presenter]