From ad29ee0635f20328d101ea4f0503c8ba6b3681d6 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 1 Mar 2023 15:18:35 +0100 Subject: [PATCH 01/47] di: added Global State and Singletons --- dependency-injection/bg/@home.texy | 3 +- dependency-injection/bg/@left-menu.texy | 3 +- dependency-injection/bg/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/cs/@home.texy | 3 +- dependency-injection/cs/@left-menu.texy | 3 +- dependency-injection/cs/container.texy | 2 +- dependency-injection/cs/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/de/@home.texy | 3 +- dependency-injection/de/@left-menu.texy | 3 +- dependency-injection/de/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/el/@home.texy | 3 +- dependency-injection/el/@left-menu.texy | 3 +- dependency-injection/el/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/en/@home.texy | 3 +- dependency-injection/en/@left-menu.texy | 3 +- dependency-injection/en/container.texy | 2 +- dependency-injection/en/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/es/@home.texy | 3 +- dependency-injection/es/@left-menu.texy | 3 +- dependency-injection/es/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/fr/@home.texy | 3 +- dependency-injection/fr/@left-menu.texy | 3 +- dependency-injection/fr/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/hu/@home.texy | 3 +- dependency-injection/hu/@left-menu.texy | 3 +- dependency-injection/hu/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/it/@home.texy | 3 +- dependency-injection/it/@left-menu.texy | 3 +- dependency-injection/it/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/it/introduction.texy | 4 +- dependency-injection/ja/@home.texy | 3 +- dependency-injection/ja/@left-menu.texy | 3 +- dependency-injection/ja/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/pl/@home.texy | 3 +- dependency-injection/pl/@left-menu.texy | 3 +- dependency-injection/pl/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/pt/@home.texy | 3 +- dependency-injection/pt/@left-menu.texy | 3 +- dependency-injection/pt/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/ro/@home.texy | 3 +- dependency-injection/ro/@left-menu.texy | 3 +- dependency-injection/ro/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/ru/@home.texy | 3 +- dependency-injection/ru/@left-menu.texy | 3 +- dependency-injection/ru/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/sl/@home.texy | 3 +- dependency-injection/sl/@left-menu.texy | 3 +- dependency-injection/sl/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/tr/@home.texy | 3 +- dependency-injection/tr/@left-menu.texy | 3 +- dependency-injection/tr/global-state.texy | 312 ++++++++++++++++++++++ dependency-injection/uk/@home.texy | 3 +- dependency-injection/uk/@left-menu.texy | 3 +- dependency-injection/uk/global-state.texy | 312 ++++++++++++++++++++++ 54 files changed, 5376 insertions(+), 38 deletions(-) create mode 100644 dependency-injection/bg/global-state.texy create mode 100644 dependency-injection/cs/global-state.texy create mode 100644 dependency-injection/de/global-state.texy create mode 100644 dependency-injection/el/global-state.texy create mode 100644 dependency-injection/en/global-state.texy create mode 100644 dependency-injection/es/global-state.texy create mode 100644 dependency-injection/fr/global-state.texy create mode 100644 dependency-injection/hu/global-state.texy create mode 100644 dependency-injection/it/global-state.texy create mode 100644 dependency-injection/ja/global-state.texy create mode 100644 dependency-injection/pl/global-state.texy create mode 100644 dependency-injection/pt/global-state.texy create mode 100644 dependency-injection/ro/global-state.texy create mode 100644 dependency-injection/ru/global-state.texy create mode 100644 dependency-injection/sl/global-state.texy create mode 100644 dependency-injection/tr/global-state.texy create mode 100644 dependency-injection/uk/global-state.texy diff --git a/dependency-injection/bg/@home.texy b/dependency-injection/bg/@home.texy index 4dec44c7dc..bb74d8c33a 100644 --- a/dependency-injection/bg/@home.texy +++ b/dependency-injection/bg/@home.texy @@ -5,8 +5,9 @@ Dependency Injection е шаблон за проектиране, който ще промени из основи начина, по който гледате на кода и разработката. Той открива пътя към свят на чисто проектирани и устойчиви приложения. - [Какво е вкарване на зависимости? |introduction] -- [Какво е DI контейнер? |container] +- [Глобално състояние и единични елементи |global-state] - [Предаване на зависимости |passing-dependencies] +- [Какво е DI контейнер? |container] Nette DI diff --git a/dependency-injection/bg/@left-menu.texy b/dependency-injection/bg/@left-menu.texy index e2dceb8d8b..545c3699e4 100644 --- a/dependency-injection/bg/@left-menu.texy +++ b/dependency-injection/bg/@left-menu.texy @@ -1,8 +1,9 @@ Изпълнение на зависимостта ************************** - [Какво е прилагане на зависимостта? |introduction] -- [Какво е контейнер DI? |container] +- [Глобално състояние и единични елементи |global-state] - [Прехвърляне на зависимостта |passing-dependencies] +- [Какво е контейнер DI? |container] Nette DI diff --git a/dependency-injection/bg/global-state.texy b/dependency-injection/bg/global-state.texy new file mode 100644 index 0000000000..c3c95d1be7 --- /dev/null +++ b/dependency-injection/bg/global-state.texy @@ -0,0 +1,312 @@ +Глобално състояние и единични числа +*********************************** + +.[perex] +Предупреждение: Следните конструкции са симптоми на лош дизайн на кода: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` или `static::$var` + +Среща ли се някоя от тези конструкции във вашия код? Тогава имате възможност да се подобрите. Може би си мислите, че това са често срещани конструкции, които виждаме в примерни решения на различни библиотеки и фреймуърки. +За съжаление, те все още са ясен индикатор за лош дизайн. Те имат една обща черта: използването на глобално състояние. + +Сега със сигурност не става дума за някаква академична чистота. Използването на глобално състояние и синглетони има разрушителен ефект върху качеството на кода. Поведението му става непредсказуемо, намалява производителността на разработчиците и принуждава интерфейсите на класовете да лъжат за истинските си зависимости. Което обърква програмистите. + +В тази глава ще покажем как това е възможно. + + +Глобално свързване .[#toc-global-interlinking] +---------------------------------------------- + +Основният проблем на глобалната държава е, че тя е глобално достъпна. Това дава възможност да се записва в базата данни чрез глобалния (статичен) метод `DB::insert()`. +В един идеален свят даден обект трябва да може да комуникира само с други обекти, които са му били [директно предадени |passing-dependencies]. +Ако създам два обекта `A` и `B` и никога не предам референция от `A` на `B`, тогава нито `A`, нито `B` могат да получат достъп до другия обект или да променят състоянието му. +Това е много желана характеристика на кода. Подобно е на това да имате батерия и електрическа крушка; крушката няма да светне, докато не ги свържете. + +Това не е вярно за глобалните (статични) променливи или единичните променливи. Обектът `A` би могъл да получи *безжичен* достъп до обекта `C` и да го модифицира, без да предава никаква референция, като извика `C::changeSomething()`. +Ако обектът `B` грабне и глобалната променлива `C`, тогава `A` и `B` могат да взаимодействат помежду си чрез `C`. + +Използването на глобални променливи въвежда нова форма на *безжично* свързване в системата, която не е видима отвън. +Тя създава димна завеса, която усложнява разбирането и използването на кода. +Разработчиците трябва да прочетат всеки ред от изходния код, за да разберат наистина зависимостите. Вместо просто да се запознаят с интерфейса на класовете. +Освен това това е напълно ненужно свързване. + +.[note] +По отношение на поведението няма разлика между глобална и статична променлива. Те са еднакво вредни. + + +Призрачно действие на разстояние (Spooky Action at a Distance) .[#toc-the-spooky-action-at-a-distance] +------------------------------------------------------------------------------------------------------ + +"Призрачно действие на разстояние" - така Алберт Айнщайн нарича едно явление в квантовата физика, което през 1935 г. му докарало ужас. +Става дума за квантовото заплитане, чиято особеност е, че когато измервате информация за една частица, веднага въздействате върху друга частица, дори ако те са на милиони светлинни години една от друга. +което привидно нарушава фундаменталния закон на Вселената, според който нищо не може да се движи по-бързо от светлината. + +В света на софтуера можем да наречем "spooky action at a distance" ситуация, при която стартираме процес, който смятаме за изолиран (защото не сме му предали никакви референции), но в отдалечени места на системата се случват неочаквани взаимодействия и промени в състоянието, за които не сме казали на обекта. Това може да се случи само чрез глобалното състояние. + +Представете си, че се присъедините към екип за разработване на проект, който има голяма, зряла база от кодове. Новият ви ръководител ви моли да реализирате нова функция и като добър разработчик започвате с написването на тест. Но тъй като сте нов в проекта, правите много проучвателни тестове от типа "какво ще стане, ако извикам този метод". И се опитвате да напишете следния тест: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // номера на картата ви. + $cc->charge(100); +} +``` + +Изпълнявате кода, може би няколко пъти, и след известно време забелязвате на телефона си известия от банката, че всеки път, когато го изпълнявате, от кредитната ви карта са били изтеглени 100 долара 🤦‍♂️ + +Как, за Бога, тестът би могъл да предизвика действително таксуване? Не е лесно да се оперира с кредитна карта. Трябва да взаимодействате с уеб услуга на трета страна, трябва да знаете URL адреса на тази уеб услуга, трябва да влезете в системата и т.н. +Нито една от тези информации не е включена в теста. Още по-лошо, дори не знаете къде присъства тази информация и следователно как да издекламирате външните зависимости, така че всяко изпълнение да не води до повторно таксуване на 100 USD. А като нов разработчик откъде трябваше да знаете, че това, което ще направите, ще доведе до обедняването ви със 100 долара? + +Това е призрачно действие от разстояние! + +Не ви остава нищо друго, освен да се ровите в много изходен код, да питате по-възрастни и по-опитни колеги, докато разберете как работят връзките в проекта. +Това се дължи на факта, че когато разглеждате интерфейса на класа `CreditCard`, не можете да определите глобалното състояние, което трябва да бъде инициализирано. Дори разглеждането на изходния код на класа няма да ви подскаже кой метод за инициализация да извикате. В най-добрия случай можете да намерите глобалната променлива, до която се осъществява достъп, и да се опитате да предположите как да я инициализирате от нея. + +Класовете в такъв проект са патологични лъжци. Платежната карта се преструва, че можете просто да я инстанцирате и да извикате метода `charge()`. Тя обаче тайно взаимодейства с друг клас, `PaymentGateway`. Дори интерфейсът му казва, че може да бъде инициализиран самостоятелно, но в действителност той тегли идентификационни данни от някакъв конфигурационен файл и така нататък. +За разработчиците, които са написали този код, е ясно, че `CreditCard` се нуждае от `PaymentGateway`. Те са написали кода по този начин. Но за всеки, който е нов в проекта, това е пълна загадка и пречи на обучението. + +Как да поправим ситуацията? Лесно. **Дайте възможност на API да декларира зависимостите.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Забележете как връзките в кода изведнъж стават очевидни. Като декларирате, че методът `charge()` се нуждае от `PaymentGateway`, не е нужно да питате никого как кодът е взаимозависим. Знаете, че трябва да създадете негова инстанция, а когато се опитате да го направите, се сблъсквате с факта, че трябва да предоставите параметри за достъп. Без тях кодът дори не би могъл да се изпълни. + +И най-важното, сега можете да издекламирате шлюза за плащане, така че да не ви таксуват по 100 долара всеки път, когато стартирате тест. + +Глобалното състояние кара обектите ви да могат тайно да получават достъп до неща, които не са декларирани в техните API, и в резултат на това прави API-тата ви патологични лъжци. + +Може и да не сте го мислили по този начин преди, но винаги когато използвате глобално състояние, създавате тайни безжични канали за комуникация. Страховитото отдалечено действие принуждава разработчиците да четат всеки ред код, за да разберат потенциалните взаимодействия, намалява производителността на разработчиците и обърква новите членове на екипа. +Ако вие сте този, който е създал кода, знаете истинските зависимости, но всеки, който идва след вас, е безпомощен. + +Не пишете код, който използва глобално състояние, а предпочитайте да предавате зависимости. Това е инжектиране на зависимости. + + +Крехкостта на глобалната държава .[#toc-brittleness-of-the-global-state] +------------------------------------------------------------------------ + +В код, който използва глобално състояние и единични елементи, никога не е сигурно кога и от кого е променено това състояние. Този риск е налице още при инициализацията. Следващият код трябва да създаде връзка с база данни и да инициализира шлюза за плащания, но продължава да хвърля изключение и намирането на причината е изключително трудоемко: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Трябва да прегледате подробно кода, за да откриете, че обектът `PaymentGateway` осъществява безжичен достъп до други обекти, някои от които изискват връзка с база данни. По този начин трябва да инициализирате базата данни, преди `PaymentGateway`. Димната завеса на глобалното състояние обаче скрива това от вас. Колко време бихте спестили, ако API на всеки клас не лъже и не декларира своите зависимости? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Подобен проблем възниква, когато използвате глобален достъп до връзка с база данни: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Когато се извиква методът `save()`, не е сигурно дали вече е създадена връзка към базата данни и кой е отговорен за създаването ѝ. Например, ако искаме да променим връзката с базата данни в движение, може би за целите на тестването, вероятно ще трябва да създадем допълнителни методи като `DB::reconnect(...)` или `DB::reconnectForTest()`. + +Разгледайте един пример: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Откъде можем да сме сигурни, че тестовата база данни наистина се използва при извикването на `$article->save()`? Какво ще стане, ако методът `Foo::doSomething()` промени глобалната връзка към базата данни? За да разберем това, ще трябва да разгледаме изходния код на класа `Foo` и вероятно на много други класове. Този подход обаче би дал само краткосрочен отговор, тъй като ситуацията може да се промени в бъдеще. + +Какво ще стане, ако преместим връзката с базата данни в статична променлива вътре в класа `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Това изобщо не променя нищо. Проблемът е глобално състояние и няма значение в кой клас се крие. В този случай, както и в предишния, нямаме представа в коя база данни се записва, когато се извика методът `$article->save()`. Всеки, който се намира в далечния край на приложението, може да промени базата данни по всяко време, като използва `Article::setDb()`. Под нашите ръце. + +Глобалното състояние прави нашето приложение **изключително крехко**. + +Има обаче прост начин да се справим с този проблем. Просто накарайте API да декларира зависимости, за да се осигури правилна функционалност. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Този подход елиминира притеснението от скрити и неочаквани промени във връзките с бази данни. Сега сме сигурни къде се съхранява статията и никакви модификации на кода вътре в друг несвързан клас вече не могат да променят ситуацията. Кодът вече не е крехък, а стабилен. + +Не пишете код, който използва глобално състояние, а предпочитайте да предавате зависимости. По този начин се въвежда инжектиране на зависимости. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton е шаблон за проектиране, който по [дефиниция от |https://en.wikipedia.org/wiki/Singleton_pattern] известната публикация на Gang of Four ограничава класа до един екземпляр и предлага глобален достъп до него. Реализацията на този шаблон обикновено прилича на следния код: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // и други методи, които изпълняват функциите на класа. +} +``` + +За съжаление сингълтонът въвежда глобално състояние в приложението. А както показахме по-горе, глобалното състояние е нежелателно. Ето защо сингълтонът се счита за антимодел. + +Не използвайте сингълтони в кода си и ги заменете с други механизми. Наистина не се нуждаете от синглетони. Ако обаче трябва да гарантирате съществуването на една единствена инстанция на даден клас за цялото приложение, оставете това на [DI контейнера |container]. +По този начин създайте синглетон на приложението или услуга. Това ще попречи на класа да осигури собствената си уникалност (т.е. той няма да има `getInstance()` метод и статична променлива) и ще изпълнява само своите функции. По този начин той ще спре да нарушава принципа на единичната отговорност. + + +Глобално състояние срещу тестове .[#toc-global-state-versus-tests] +------------------------------------------------------------------ + +Когато пишем тестове, приемаме, че всеки тест е изолирана единица и че в него не влиза никакво външно състояние. И никое състояние не напуска тестовете. Когато тестът завърши, всяко състояние, свързано с него, трябва да бъде премахнато автоматично от garbage collector. Това прави тестовете изолирани. Следователно можем да изпълняваме тестовете в произволен ред. + +Ако обаче са налице глобални състояния/синглетони, всички тези хубави предположения се развалят. Едно състояние може да влиза и излиза от тест. Изведнъж редът на тестовете може да има значение. + +За да могат изобщо да тестват единични състояния, разработчиците често трябва да смекчат техните свойства, може би като позволят даден екземпляр да бъде заменен с друг. Такива решения в най-добрия случай са хакове, които създават код, който е труден за поддържане и разбиране. Всеки тест или метод `tearDown()`, който засяга някое глобално състояние, трябва да отмени тези промени. + +Глобалното състояние е най-голямото главоболие при тестването на единици! + +Как да поправим ситуацията? Лесно. Не пишете код, който използва singletons, а предпочитайте да предавате зависимости. Това е инжектиране на зависимости. + + +Глобални константи .[#toc-global-constants] +------------------------------------------- + +Глобалното състояние не се ограничава само до използването на единични и статични променливи, но може да се прилага и за глобални константи. + +Константи, чиято стойност не ни предоставя никаква нова (`M_PI`) или полезна (`PREG_BACKTRACK_LIMIT_ERROR`) информация, са очевидно ОК. +Обратно, константи, които служат като начин за *безжично* предаване на информация вътре в кода, не са нищо повече от скрита зависимост. Като `LOG_FILE` в следния пример. +Използването на константата `FILE_APPEND` е напълно правилно. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +В този случай трябва да декларираме параметъра в конструктора на класа `Foo`, за да го направим част от API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Сега можем да подаваме информация за пътя до файла за регистриране и лесно да го променяме при необходимост, което улеснява тестването и поддръжката на кода. + + +Глобални функции и статични методи .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------------------ + +Искаме да подчертаем, че използването на статични методи и глобални функции само по себе си не е проблематично. Обяснихме неуместността на използването на `DB::insert()` и други подобни методи, но винаги е ставало въпрос за глобално състояние, съхранявано в статична променлива. Методът `DB::insert()` изисква съществуването на статична променлива, защото съхранява връзката с базата данни. Без тази променлива би било невъзможно да се реализира методът. + +Използването на детерминистични статични методи и функции, като например `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` и много други, е напълно съвместимо с инжектирането на зависимости. Тези функции винаги връщат едни и същи резултати от едни и същи входни параметри и следователно са предвидими. Те не използват никакво глобално състояние. + +В PHP обаче има функции, които не са детерминирани. Сред тях е например функцията `htmlspecialchars()`. Нейният трети параметър, `$encoding`, ако не е посочен, по подразбиране е стойността на конфигурационната опция `ini_get('default_charset')`. Ето защо се препоръчва винаги да посочвате този параметър, за да избегнете евентуално непредсказуемо поведение на функцията. Nette последователно прави това. + +Някои функции, като например `strtolower()`, `strtoupper()` и други подобни, имаха недетерминирано поведение в близкото минало и зависеха от настройката `setlocale()`. Това предизвикваше много усложнения, най-често при работа с турския език. +Това е така, защото турският език прави разлика между големи и малки букви `I` с и без точка. Така `strtolower('I')` връщаше символа `ı`, а `strtoupper('i')` - символа `İ`, което водеше до това, че приложенията предизвикваха редица мистериозни грешки. +Този проблем обаче беше отстранен във версия 8.2 на PHP и функциите вече не зависят от локалите. + +Това е хубав пример за това как глобалното състояние е измъчвало хиляди разработчици по целия свят. Решението беше да го заменим с инжектиране на зависимости. + + +Кога е възможно да се използва глобално състояние? .[#toc-when-is-it-possible-to-use-global-state] +-------------------------------------------------------------------------------------------------- + +Има някои специфични ситуации, в които е възможно да се използва глобално състояние. Например, когато отстранявате грешки в кода и трябва да изхвърлите стойността на променлива или да измерите продължителността на определена част от програмата. В такива случаи, които се отнасят до временни действия, които по-късно ще бъдат премахнати от кода, е закономерно да се използва глобално достъпен дъмпер или хронометър. Тези инструменти не са част от дизайна на кода. + +Друг пример са функциите за работа с регулярни изрази `preg_*`, които вътрешно съхраняват компилирани регулярни изрази в статичен кеш в паметта. Когато извиквате един и същ регулярен израз няколко пъти в различни части на кода, той се компилира само веднъж. Кешът спестява производителност, а освен това е напълно невидим за потребителя, така че такава употреба може да се счита за легитимна. + + +Обобщение .[#toc-summary] +------------------------- + +Показахме защо има смисъл + +1) Премахнете всички статични променливи от кода +2) Декларирайте зависимостите +3) И използвайте инжектиране на зависимости + +Когато обмисляте дизайна на кода, имайте предвид, че всеки `static $foo` представлява проблем. За да може кодът ви да бъде среда, уважаваща DI, е необходимо напълно да изкорените глобалното състояние и да го замените с инжектиране на зависимости. + +По време на този процес може да откриете, че трябва да разделите даден клас, защото той има повече от една отговорност. Не се притеснявайте за това; стремете се към принципа на една отговорност. + +*Искам да благодаря на Мишко Хевери, чиито статии като [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] са в основата на тази глава.* diff --git a/dependency-injection/cs/@home.texy b/dependency-injection/cs/@home.texy index 3e66ae0454..8f5b0b4ab3 100644 --- a/dependency-injection/cs/@home.texy +++ b/dependency-injection/cs/@home.texy @@ -5,8 +5,9 @@ Dependency Injection Dependency Injection je návrhový vzor, který zásadně změní váš pohled na kód a vývoj. Otevře vám cestu do světa čistě navržených a udržitelných aplikací. - [Co je Dependency Injection? |introduction] -- [Co je DI kontejner? |container] +- [Globální stav a singletony |global-state] - [Předávání závislostí |passing-dependencies] +- [Co je DI kontejner? |container] Nette DI diff --git a/dependency-injection/cs/@left-menu.texy b/dependency-injection/cs/@left-menu.texy index f4bade0b57..d1c09b217f 100644 --- a/dependency-injection/cs/@left-menu.texy +++ b/dependency-injection/cs/@left-menu.texy @@ -1,8 +1,9 @@ Dependency Injection ******************** - [Co je DI? |introduction] -- [Co je DI kontejner? |container] +- [Globální stav a singletony |global-state] - [Předávání závislostí |passing-dependencies] +- [Co je DI kontejner? |container] Nette DI diff --git a/dependency-injection/cs/container.texy b/dependency-injection/cs/container.texy index 477e4be064..3583a27dad 100644 --- a/dependency-injection/cs/container.texy +++ b/dependency-injection/cs/container.texy @@ -4,7 +4,7 @@ Co je DI kontejner? .[perex] Dependency injection kontejner (DIC) je třída, která umí instancovat a konfigurovat objekty. -Možná vás to překvapí, ale v mnoha případech nepotřebujete dependency injection kontejner, abyste mohli využívat výhod dependency injection (krátce DI). Vždyť i v [předchozí kapitole|introduction] jsme si na konkrétních příkladech DI ukázali a žádný kontejner nebyl potřeba. +Možná vás to překvapí, ale v mnoha případech nepotřebujete dependency injection kontejner, abyste mohli využívat výhod dependency injection (krátce DI). Vždyť i v [úvodní kapitole|introduction] jsme si na konkrétních příkladech DI ukázali a žádný kontejner nebyl potřeba. Pokud však potřebujete spravovat velké množství různých objektů s mnoha závislostmi, bude dependency injection container opravdu užitečný. Což je třeba případ webových aplikací postavených na frameworku. diff --git a/dependency-injection/cs/global-state.texy b/dependency-injection/cs/global-state.texy new file mode 100644 index 0000000000..09193a246b --- /dev/null +++ b/dependency-injection/cs/global-state.texy @@ -0,0 +1,312 @@ +Globální stav a singletony +************************** + +.[perex] +Varování: Následující konstrukce jsou příznakem špatného návrhu kódu: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` nebo `static::$var` + +Vyskytují se některé z těchto konstrukcí ve vašem kódu? Pak máte příležitost k zlepšení. Možná si říkáte, že jde o běžné konstrukce, které vídáme i v ukázkových řešeních různých knihoven a frameworků. +Bohužel, i přesto jsou jasným indikátorem špatného návrhu. Spojuje je jedno: používání globálního stavu. + +Nyní rozhodně nemluvíme o jakési akademické čistotě. Používání globálního stavu a singletonů má destruktivní dopady na kvalitu kódu. Jeho chování se stává nepředvídatelné, snižuje produktivitu vývojářů a nutí rozhraní tříd lhát o svých skutečných závislostech. Což mate programátory. + +V této kapitole si ukážeme, jak je to možné. + + +Globální provázání +------------------ + +Základní problém globálního stavu spočívá v tom, že je globálně přístupný. Díky tomu je třeba možné zapsat do databáze přes globální (statickou) metodu `DB::insert()`. +V ideálním světě by měl být objekt schopen komunikovat pouze s jinými objekty, které mu byly [přímo předány |passing-dependencies]. +Pokud vytvořím dva objekty `A` a `B` a nikdy nepředám referenci z `A` na `B`, pak se ani `A`, ani `B` nemohou dostat k druhému objektu nebo změnit jeho stav. +To je velmi žádoucí vlastnost kódu. Je to podobné, jako když máte baterii a žárovku; žárovka nebude svítit, dokud je nepropojíte drátem. + +To ale neplatí u globálních (statických) proměnných nebo singletonů. Objekt `A` by se mohl *bezdrátově* dostat k objektu `C` a modifikovat jej bez jakéhokoliv předání reference, tím, že zavolá `C::changeSomething()`. +Pokud se objekt `B` také chopí globálního `C`, pak se `A` a `B` mohou navzájem ovlivňovat prostřednictvím `C`. + +Použití globálních proměnných do systému vnáší novou formu *bezdrátové* provázanosti, která není zvenčí vidět. +Vytváří kouřovou clonu komplikující pochopení a používání kódu. +Aby vývojáři závislostem skutečně porozuměli, musí přečíst každý řádek zdrojového kódu. Místo pouhého seznámení se s rozhraním tříd. +Jde navíc o provázanost zcela zbytečnou. + +.[note] +Z hlediska chování není rozdíl mezi globální a statickou proměnnou. Jsou stejně škodlivé. + + +Strašidelné působení na dálku +----------------------------- + +"Strašidelné působení na dálku" - tak slavně nazval roku 1935 Albert Einstein jev v kvantové fyzice, který mu naháněl husí kůži. +Jedná se o kvantové propojení, jehož zvláštností je, že když změříte informaci o jedné částici, okamžitě tím ovlivníte částici druhou, i když jsou od sebe vzdáleny miliony světelných let. +Což zdánlivě porušuje základní zákon vesmíru, že nic se nemůže šířit rychleji než světlo. + +V softwarovém světě můžeme "strašidelným působení na dálku" nazvat situaci, kdy spustíme nějaký proces, o kterém se domníváme, že je izolovaný (protože jsme mu nepředali žádné reference), ale ve vzdálených místech systému dojde k neočekávaným interakcím a změnám stavu, o kterých jsme neměli tušení. K tomu může dojít pouze prostřednictvím globálního stavu. + +Představte si, že se připojíte k týmu vývojářů projektu, který má rozsáhlou vyspělou kódovou základnu. Váš nový vedoucí vás požádá o implementaci nové funkce a vy jako správný vývojář začnete psaním testu. Protože jste ale v projektu noví, děláte spoustu průzkumných testů typu "co se stane, když zavolám tuto metodu". A zkusíte napsat následující test: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // číslo vaší karty + $cc->charge(100); +} +``` + +Spustíte kód, třeba několikrát, a po nějaké době si všimnete na mobilu notifikací z banky, že při každém spuštění se strhlo 100 dolarů z vaší platební karty 🤦‍♂️ + +Jak proboha mohl test způsobit skutečné stržení peněz? Operovat s platební kartou není snadné. Musíte komunikovat s webovou službou třetí strany, musíte znát URL této webové služby, musíte se přihlásit a tak dále. +Žádná z těchto informací není v testu obsažena. Ba co hůř, ani nevíte, kde jsou tyto informace přítomny, a tedy ani jak mockovat externí závislosti, aby každé spuštění nevedlo k tomu, že se znovu strhne 100 dolarů. A jak jste měl jako nový vývojář vědět, že to, co se chystáte udělat, povede k tomu, že budete o 100 dolarů chudší? + +To je strašidelné působení na dálku! + +Nezbývá vám, než se dlouze hrabat ve spoustě zdrojových kódů, ptát se starších a zkušenějších kolegů, než pochopíte, jak vazby v projektu fungují. +To je způsobeno tím, že při pohledu na rozhraní třídy `CreditCard` nelze zjistit globální stav, který je třeba inicializovat. Dokonce ani pohled do zdrojového kódu třídy vám neprozradí, kterou inicializační metodu máte zavolat. V nejlepším případě můžete najít globální proměnnou, ke které se přistupuje, a z ní se pokusit odhadnout, jak ji inicializovat. + +Třídy v takovém projektu jsou patologickými lháři. Platební karta předstírá, že ji stačí instancovat a zavolat metodu `charge()`. Ve skrytu však spolupracuje s jinou třídou `PaymentGateway`, která představuje platební bránu. I její rozhraní říká, že ji lze inicializovat samostatně, ale ve skutečnosti si vytáhne credentials z nějakého konfiguračního souboru a tak dále. +Vývojářům, kteří tento kód napsali, je jasné, že `CreditCard` potřebuje `PaymentGateway`. Napsali kód tímto způsobem. Ale pro každého, kdo je v projektu nový, je to naprostá záhada a brání to učení. + +Jak situaci opravit? Snadno. **Nechte API deklarovat závislosti.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Všimněte si, jak jsou najednou provázanosti uvnitř kódu zřejmé. Tím, že metoda `charge()` deklaruje, že potřebuje `PaymentGateway`, nemusíte se na to, jak je kód provázaný, nikoho ptát. Víte, že musíte vytvořit její instanci, a když se o to pokusíte, narazíte na to, že musíte dodat přístupové parametry. Bez nich by kód nešel ani spustit. + +A hlavně nyní můžete platební bránu mockovat, takže se vám při každém spuštění testu nebude účtovat 100 dolarů. + +Globální stav způsobuje, že se vaše objekty mohou tajně dostat k věcem, které nejsou deklarovány v jejich API, a v důsledku toho dělají z vašich API patologické lháře. + +Možná jste o tom dříve takto nepřemýšleli, ale kdykoli používáte globální stav, vytváříte tajné bezdrátové komunikační kanály. Strašidelná akce na dálku nutí vývojáře číst každý řádek kódu, aby pochopili potenciální interakce, snižuje produktivitu vývojářů a mate nové členy týmu. +Pokud jste vy ten, kdo kód vytvořil, znáte skutečné závislosti, ale každý, kdo přijde po vás, je bezradný. + +Nepište kód, který využívá globální stav, dejte přednost předávání závislostí. Tedy dependency injection. + + +Křehkost globálního stavu +------------------------- + +V kódu, který používá globální stav a singletony, není nikdy jisté, kdy a kdo tento stav změnil. Toto riziko se objevuje již při inicializaci. Následující kód má vytvořit databázové spojení a inicializovat platební bránu, avšak neustále vyhazuje výjimku a hledání příčiny je nesmírně zdlouhavé: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Musíte podrobně procházet kód, abyste zjistili, že objekt `PaymentGateway` přistupuje bezdrátově k dalším objektům, z nichž některé vyžadují databázové připojení. Tedy je nutné inicializovat databázi dříve než `PaymentGateway`. Nicméně kouřová clona globálního stavu toto před vámi skrývá. Kolik času byste ušetřili, kdyby API jednotlivých tříd neklamalo a deklarovalo své závislosti? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Podobný problém se objevuje i při použití globálního přístupu k databázovému spojení: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Při volání metody `save()` není jisté, zda bylo již vytvořeno připojení k databázi a kdo nese odpovědnost za jeho vytvoření. Pokud chceme například měnit databázové připojení za běhu, třeba kvůli testům, museli bychom nejspíš vytvořit další metody jako například `DB::reconnect(...)` nebo `DB::reconnectForTest()`. + +Zvažme příklad: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Kde máme jistotu, že při volání `$article->save()` se opravdu používá testovací databáze? Co když metoda `Foo::doSomething()` změnila globální databázové připojení? Pro zjištění bychom museli prozkoumat zdrojový kód třídy `Foo` a pravděpodobně i mnoha dalších tříd. Tento přístup by však přinesl pouze krátkodobou odpověď, jelikož se situace může v budoucnu změnit. + +A co když připojení k databázi přesuneme do statické proměnné uvnitř třídy `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Tím se vůbec nic nezměnilo. Problémem je globální stav a je úplně jedno, ve které třídě se skrývá. V tomto případě, stejně jako v předchozím, nemáme při volání metody `$article->save()` žádné vodítko k tomu, do jaké databáze se zapíše. Kdokoliv na druhém konci aplikace mohl kdykoliv pomocí `Article::setDb()` databázi změnit. Nám pod rukama. + +Globálnímu stav činní naši aplikaci **nesmírně křehkou**. + +Existuje však jednoduchý způsob, jak s tímto problémem naložit. Stačí nechat API deklarovat závislosti, čímž se zajistí správná funkčnost. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Díky tomuto přístupu odpadá obava o skryté a neočekávané změny připojení k databázi. Nyní máme jistotu, kam se článek ukládá a žádné úpravy kódu uvnitř jiné nesouvisející třídy již nemohou situaci změnit. Kód už není křehký, ale stabilní. + +Nepište kód, který využívá globální stav, dejte přednost předávání závislostí. Tedy dependency injection. + + +Singleton +--------- + +Singleton je návrhový vzor, který podle "definice":https://en.wikipedia.org/wiki/Singleton_pattern ze známé publikace Gang of Four omezuje třídu na jedinou instanci a nabízí k ní globální přístup. Implementace tohoto vzoru se obvykle podobá následujícímu kódu: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // a další metody plnící funkce dané třídy +} +``` + +Bohužel, singleton zavádí do aplikace globální stav. A jak jsme si ukázali výše, globální stav je nežádoucí. Proto je singleton považován za antipattern. + +Nepoužívejte ve svém kódu singletony a nahraďte je jinými mechanismy. Singletony opravdu nepotřebujete. Pokud však potřebujete zaručit existenci jediné instance třídy pro celou aplikaci, nechte to na [DI kontejneru |container]. +Vytvořte tak aplikační singleton, neboli službu. Tím se třída přestane věnovat zajištění své vlastní jedinečnosti (tj. nebude mít metodu `getInstance()` a statickou proměnnou) a bude plnit pouze své funkce. Tak přestane porušovat princip jediné odpovědnosti. + + +Globální stav versus testy +-------------------------- + +Při psaní testů předpokládáme, že každý test je izolovanou jednotkou a že do něj nevstupuje žádný externí stav. A žádný stav testy neopouští. Po dokončení testu by měl být veškerý související stav s testem odstraněn automaticky garbage collectorem. Díky tomu jsou testy izolované. Proto můžeme testy spouštět v libovolném pořadí. + +Pokud jsou však přítomny globální stavy/singletony, všechny tyto příjemné předpoklady se rozpadají. Stav může do testu vstupovat a vystupovat z něj. Najednou může záležet na pořadí testů. + +Abychom vůbec mohli testovat singletony, vývojáři často musí rozvolnit jejich vlastnosti, třeba tím, že dovolí instanci nahradit jinou. Taková řešení jsou v nejlepším případě hackem, který vytváří obtížně udržovatelný a srozumitelný kód. Každý test nebo metoda `tearDown()`, která ovlivní jakýkoli globální stav, musí tyto změny vrátit zpět. + +Globální stav je největší bolestí hlavy při unit testování! + +Jak situaci opravit? Snadno. Nepište kód, který využívá singletony, dejte přednost předávání závislostí. Tedy dependency injection. + + +Globální konstanty +------------------ + +Globální stav se neomezuje pouze na používání singletonů a statických proměnných, ale může se týkat také globálních konstant. + +Konstanty, jejichž hodnota nám nepřináší žádnou novou (`M_PI`) nebo užitečnou (`PREG_BACKTRACK_LIMIT_ERROR`) informaci, jsou jednoznačně v pořádku. +Naopak konstanty, které slouží jako způsob, jak *bezdrátově* předat informaci dovnitř kódu, nejsou ničím jiným než skrytou závislostí. Jako třeba `LOG_FILE` v následujícím příkladu. +Použití konstanty `FILE_APPEND` je zcela korektní. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +V tomto případě bychom měli deklarovat parametr v konstruktoru třídy `Foo`, aby se stal součástí API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Nyní můžeme předat informaci o cestě k souboru pro logování a snadno ji měnit podle potřeby, což usnadňuje testování a údržbu kódu. + + +Globální funkce a statické metody +--------------------------------- + +Chceme zdůranit, že samotné používání statických metod a globálních funkcí není problematické. Vysvětlovali jsme, v čem spočívá nevhodnost použití `DB::insert()` a podobných metod, ale vždy se jednalo pouze o záležitost globálního stavu, který je uložen v nějaké statické proměnné. Metoda `DB::insert()` vyžaduje existenci statické proměnné, protože v ní je uloženo připojení k databázi. Bez této proměnné by bylo nemožné metodu implementovat. + +Používání deterministických statických metod a funkcí, jako například `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` a mnoha dalších, je v naprostém souladu s dependency injection. Tyto funkce vždy vracejí stejné výsledky ze stejných vstupních parametrů a jsou tedy předvídatelné. Nepoužívají žádný globální stav. + +Existují ovšem i funkce v PHP, které nejsou deterministické. K nim patří například funkce `htmlspecialchars()`. Její třetí parametr `$encoding`, pokud není uveden, jako výchozí hodnotu má hodnotu konfigurační volby `ini_get('default_charset')`. Proto se doporučuje tento parametr vždy uvádět a předejít tak případnému nepředvídatelnému chování funkce. Nette to důsledně dělá. + +Některé funkce, jako například `strtolower()`, `strtoupper()` a podobné, se v nedávné minulosti nedeterministicky chovaly a byly závislé na nastavení `setlocale()`. To způsobovalo mnoho komplikací, nejčastěji při práci s tureckým jazykem. +Ten totiž rozlišuje malé i velké písmeno `I` s tečkou i bez tečky. Takže `strtolower('I')` vracelo znak `ı` a `strtoupper('i')` znak `İ`, což vedlo k tomu, že aplikace začaly způsobovat řadu záhadných chyb. +Tento problém byl však odstraněn v PHP verze 8.2 a funkce již nejsou závislé na locale. + +Jde o pěkný příklad, jak globální stav potrápil tisíce vývojářů na celém světě. Řešením bylo nahradit jej za dependency injection. + + +Kdy je možné použít globální stav? +---------------------------------- + +Existují určité specifické situace, kdy je možné využít globální stav. Například při ladění kódu, když potřebujete vypsat hodnotu proměnné nebo změřit dobu trvání určité části programu. V takových případech, které se týkají dočasných akcí, jež budou později odstraněny z kódu, je možné legitimně využít globálně dostupný dumper nebo stopky. Tyto nástroje totiž nejsou součástí návrhu kódu. + +Dalším příkladem jsou funkce pro práci s regulárními výrazy `preg_*`, které interně ukládají zkompilované regulární výrazy do statické cache v paměti. Když tedy voláte stejný regulární výraz vícekrát na různých místech kódu, zkompiluje se pouze jednou. Cache šetří výkon a zároveň je pro uživatele zcela neviditelná, proto lze takové využití považovat za legitimní. + + +Shrnutí +------- + +Probrali jsme si, proč má smysl: + +1) Odstranit veškeré statické proměnné z kódu +2) Deklarovat závislosti +3) A používat dependency injection + +Když promýšlíte návrh kódu, myslete na to, že každé `static $foo` představuje problém. Aby váš kód byl prostředím respektujícím DI, je nezbytné úplně vymýtit globální stav a nahradit ho pomocí dependency injection. + +Během tohoto procesu možná zjistíte, že je třeba třídu rozdělit, protože má více než jednu odpovědnost. Nebojte se toho; usilujte o princip jedné odpovědnosti. + +*Rád bych poděkoval Miškovi Heverymu, jehož články, jako je [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/], jsou základem této kapitoly.* diff --git a/dependency-injection/de/@home.texy b/dependency-injection/de/@home.texy index 83da7cef4c..e1fe9fdf5e 100644 --- a/dependency-injection/de/@home.texy +++ b/dependency-injection/de/@home.texy @@ -5,8 +5,9 @@ Injektion von Abhängigkeiten Dependency Injection ist ein Entwurfsmuster, das die Art und Weise, wie Sie Code und Entwicklung betrachten, grundlegend verändern wird. Es öffnet den Weg in eine Welt der sauber gestalteten und nachhaltigen Anwendungen. - [Was ist Dependency Injection? |introduction] -- [Was ist ein DI-Container? |container] +- [Globaler Zustand & Singletons |global-state] - [Übergabe von Abhängigkeiten |passing-dependencies] +- [Was ist ein DI-Container? |container] Nette DI diff --git a/dependency-injection/de/@left-menu.texy b/dependency-injection/de/@left-menu.texy index be8495e1df..6c380c90d1 100644 --- a/dependency-injection/de/@left-menu.texy +++ b/dependency-injection/de/@left-menu.texy @@ -1,8 +1,9 @@ Injektion von Abhängigkeiten **************************** - [Was ist DI? |introduction] -- [Was ist ein DI-Container? |container] +- [Globaler Zustand & Singletons |global-state] - [Übergabe von Abhängigkeiten |passing-dependencies] +- [Was ist ein DI-Container? |container] Nette DI diff --git a/dependency-injection/de/global-state.texy b/dependency-injection/de/global-state.texy new file mode 100644 index 0000000000..9b3d9d680b --- /dev/null +++ b/dependency-injection/de/global-state.texy @@ -0,0 +1,312 @@ +Globaler Zustand und Singletons +******************************* + +.[perex] +Warnung: Die folgenden Konstrukte sind Symptome für schlechtes Code-Design: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` oder `static::$var` + +Kommt eines dieser Konstrukte in Ihrem Code vor? Dann haben Sie die Möglichkeit, sich zu verbessern. Sie denken vielleicht, dass es sich um gängige Konstrukte handelt, die wir in Musterlösungen verschiedener Bibliotheken und Frameworks sehen. +Leider sind sie dennoch ein klarer Indikator für schlechtes Design. Sie haben eines gemeinsam: die Verwendung eines globalen Zustands. + +Wir sprechen hier sicherlich nicht von einer akademischen Reinheit. Die Verwendung von globalen Zuständen und Singletons hat zerstörerische Auswirkungen auf die Codequalität. Ihr Verhalten wird unvorhersehbar, verringert die Produktivität der Entwickler und zwingt Klassenschnittstellen dazu, über ihre wahren Abhängigkeiten zu lügen. Das verwirrt die Programmierer. + +In diesem Kapitel werden wir zeigen, wie dies möglich ist. + + +Globale Verkettung .[#toc-global-interlinking] +---------------------------------------------- + +Das grundsätzliche Problem mit dem globalen Zustand ist, dass er global zugänglich ist. Dadurch ist es möglich, über die globale (statische) Methode `DB::insert()` in die Datenbank zu schreiben. +In einer idealen Welt sollte ein Objekt nur mit anderen Objekten kommunizieren können, die [ihm direkt übergeben |passing-dependencies] wurden. +Wenn ich zwei Objekte `A` und `B` anlege und niemals eine Referenz von `A` an `B` übergebe, dann können weder `A` noch `B` auf das andere Objekt zugreifen oder dessen Zustand ändern. +Dies ist eine sehr wünschenswerte Eigenschaft des Codes. Es ist vergleichbar mit einer Batterie und einer Glühbirne; die Glühbirne leuchtet erst, wenn man sie miteinander verbindet. + +Dies gilt nicht für globale (statische) Variablen oder Singletons. Das Objekt `A` kann *drahtlos* auf das Objekt `C` zugreifen und es ändern, ohne eine Referenz zu übergeben, indem es `C::changeSomething()` aufruft. +Wenn das Objekt `B` auch auf das globale `C` zugreift, dann können `A` und `B` über `C` miteinander interagieren. + +Die Verwendung von globalen Variablen führt eine neue Form der *drahtlosen* Kopplung in das System ein, die von außen nicht sichtbar ist. +Sie schafft einen Nebelschleier, der das Verständnis und die Verwendung des Codes erschwert. +Die Entwickler müssen jede Zeile des Quellcodes lesen, um die Abhängigkeiten wirklich zu verstehen. Anstatt sich nur mit der Schnittstelle der Klassen vertraut zu machen. +Außerdem handelt es sich um eine völlig unnötige Kopplung. + +.[note] +Was das Verhalten angeht, gibt es keinen Unterschied zwischen einer globalen und einer statischen Variable. Sie sind gleichermaßen schädlich. + + +Die spukhafte Wirkung in der Ferne .[#toc-the-spooky-action-at-a-distance] +-------------------------------------------------------------------------- + +"Spukhafte Fernwirkung" - so nannte Albert Einstein 1935 ein Phänomen der Quantenphysik, das ihm eine Gänsehaut bereitete. +Es handelt sich dabei um die Quantenverschränkung, deren Besonderheit darin besteht, dass die Messung von Informationen über ein Teilchen sofort Auswirkungen auf ein anderes Teilchen hat, selbst wenn sie Millionen von Lichtjahren voneinander entfernt sind. +Dies verstößt scheinbar gegen das grundlegende Gesetz des Universums, dass sich nichts schneller als das Licht bewegen kann. + +In der Software-Welt können wir von einer "spukhaften Fernwirkung" sprechen, wenn wir einen Prozess laufen lassen, von dem wir glauben, dass er isoliert ist (weil wir ihm keine Referenzen übergeben haben), aber unerwartete Wechselwirkungen und Zustandsänderungen an entfernten Stellen des Systems auftreten, von denen wir dem Objekt nichts gesagt haben. Dies kann nur über den globalen Zustand geschehen. + +Stellen Sie sich vor, Sie treten in ein Projektentwicklungsteam ein, das über eine große, ausgereifte Codebasis verfügt. Ihr neuer Leiter bittet Sie, eine neue Funktion zu implementieren, und wie ein guter Entwickler beginnen Sie damit, einen Test zu schreiben. Da Sie aber neu im Projekt sind, machen Sie viele Tests vom Typ "was passiert, wenn ich diese Methode aufrufe". Und Sie versuchen, den folgenden Test zu schreiben: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // Ihre Kartennummer + $cc->charge(100); +} +``` + +Sie führen den Code aus, vielleicht mehrere Male, und nach einer Weile bemerken Sie auf Ihrem Telefon Benachrichtigungen von der Bank, dass jedes Mal, wenn Sie ihn ausführen, Ihre Kreditkarte mit 100 Dollar belastet wurde 🤦‍♂️ + +Wie um alles in der Welt konnte der Test eine tatsächliche Belastung verursachen? Es ist nicht einfach, mit einer Kreditkarte zu arbeiten. Sie müssen mit einem Webdienst eines Drittanbieters interagieren, Sie müssen die URL dieses Webdienstes kennen, Sie müssen sich anmelden und so weiter. +Keine dieser Informationen ist in dem Test enthalten. Noch schlimmer ist, dass Sie nicht einmal wissen, wo diese Informationen vorhanden sind und wie Sie externe Abhängigkeiten simulieren können, damit nicht bei jedem Durchlauf erneut 100 Dollar fällig werden. Und woher sollten Sie als neuer Entwickler wissen, dass das, was Sie gerade tun wollten, Sie um 100 Dollar ärmer machen würde? + +Das ist eine gespenstische Aktion aus der Ferne! + +Es bleibt Ihnen nichts anderes übrig, als sich durch eine Menge Quellcode zu wühlen und ältere und erfahrenere Kollegen zu fragen, bis Sie verstehen, wie die Zusammenhänge im Projekt funktionieren. +Das liegt daran, dass man bei einem Blick auf die Schnittstelle der Klasse `CreditCard` den globalen Zustand, der initialisiert werden muss, nicht feststellen kann. Selbst ein Blick in den Quellcode der Klasse verrät Ihnen nicht, welche Initialisierungsmethode Sie aufrufen müssen. Bestenfalls können Sie die globale Variable finden, auf die zugegriffen wird, und versuchen, daraus zu erraten, wie sie zu initialisieren ist. + +Die Klassen in einem solchen Projekt sind pathologische Lügner. Die Zahlungskarte gibt vor, dass man sie einfach instanziieren und die Methode `charge()` aufrufen kann. Insgeheim interagiert sie jedoch mit einer anderen Klasse, `PaymentGateway`. Sogar ihre Schnittstelle sagt, dass sie unabhängig initialisiert werden kann, aber in Wirklichkeit bezieht sie Anmeldedaten aus einer Konfigurationsdatei usw. +Den Entwicklern, die diesen Code geschrieben haben, ist klar, dass `CreditCard` `PaymentGateway` benötigt. Sie haben den Code auf diese Weise geschrieben. Aber für jeden, der neu in das Projekt einsteigt, ist dies ein völliges Rätsel und behindert das Lernen. + +Wie kann man das Problem lösen? Ganz einfach. **Lassen Sie die API Abhängigkeiten deklarieren. + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Beachten Sie, dass die Beziehungen innerhalb des Codes plötzlich offensichtlich sind. Indem Sie erklären, dass die Methode `charge()` `PaymentGateway` benötigt, müssen Sie niemanden fragen, wie der Code voneinander abhängig ist. Sie wissen, dass Sie eine Instanz der Methode erstellen müssen, und wenn Sie dies versuchen, stoßen Sie auf die Tatsache, dass Sie Zugriffsparameter bereitstellen müssen. Ohne sie würde der Code nicht einmal laufen. + +Und das Wichtigste ist, dass Sie jetzt das Zahlungs-Gateway simulieren können, damit Sie nicht jedes Mal, wenn Sie einen Test durchführen, 100 Dollar bezahlen müssen. + +Der globale Status bewirkt, dass Ihre Objekte heimlich auf Dinge zugreifen können, die nicht in ihren APIs deklariert sind, und macht Ihre APIs damit zu pathologischen Lügnern. + +Sie haben vielleicht noch nie darüber nachgedacht, aber immer wenn Sie einen globalen Zustand verwenden, schaffen Sie geheime drahtlose Kommunikationskanäle. Unheimliche Remote-Aktionen zwingen Entwickler dazu, jede Codezeile zu lesen, um mögliche Interaktionen zu verstehen, verringern die Produktivität der Entwickler und verwirren neue Teammitglieder. +Wenn Sie derjenige sind, der den Code erstellt hat, kennen Sie die tatsächlichen Abhängigkeiten, aber jeder, der nach Ihnen kommt, ist ahnungslos. + +Schreiben Sie keinen Code, der globale Zustände verwendet, sondern übergeben Sie lieber Abhängigkeiten. Das heißt, Dependency Injection. + + +Die Zerbrechlichkeit des globalen Staates .[#toc-brittleness-of-the-global-state] +--------------------------------------------------------------------------------- + +Bei Code, der globale Zustände und Singletons verwendet, ist es nie sicher, wann und von wem dieser Zustand geändert wurde. Dieses Risiko ist bereits bei der Initialisierung gegeben. Der folgende Code soll eine Datenbankverbindung erstellen und das Zahlungs-Gateway initialisieren, aber er löst immer wieder eine Ausnahme aus, und die Suche nach der Ursache ist extrem mühsam: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Sie müssen den Code im Detail durchgehen, um herauszufinden, dass das Objekt `PaymentGateway` drahtlos auf andere Objekte zugreift, von denen einige eine Datenbankverbindung benötigen. Sie müssen also die Datenbank vor `PaymentGateway` initialisieren. Der Nebel des globalen Zustands verbirgt dies jedoch vor Ihnen. Wie viel Zeit würden Sie sparen, wenn die API der einzelnen Klassen nicht lügen und ihre Abhängigkeiten deklarieren würde? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Ein ähnliches Problem ergibt sich bei der Verwendung des globalen Zugriffs auf eine Datenbankverbindung: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Beim Aufruf der Methode `save()` ist nicht sicher, ob bereits eine Datenbankverbindung erstellt wurde und wer für deren Erstellung verantwortlich ist. Wenn wir zum Beispiel die Datenbankverbindung spontan ändern wollten, vielleicht zu Testzwecken, müssten wir wahrscheinlich zusätzliche Methoden wie `DB::reconnect(...)` oder `DB::reconnectForTest()` erstellen. + +Betrachten wir ein Beispiel: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Wo können wir sicher sein, dass beim Aufruf von `$article->save()` wirklich die Testdatenbank verwendet wird? Was wäre, wenn die Methode `Foo::doSomething()` die globale Datenbankverbindung ändern würde? Um das herauszufinden, müssten wir den Quellcode der Klasse `Foo` und wahrscheinlich vieler anderer Klassen untersuchen. Dieser Ansatz würde jedoch nur eine kurzfristige Antwort liefern, da sich die Situation in der Zukunft ändern kann. + +Was wäre, wenn wir die Datenbankverbindung in eine statische Variable innerhalb der Klasse `Article` verschieben? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Das ändert überhaupt nichts. Das Problem ist ein globaler Zustand und es spielt keine Rolle, in welcher Klasse es sich versteckt. In diesem Fall, wie auch im vorherigen, haben wir keine Ahnung, in welche Datenbank geschrieben wird, wenn die Methode `$article->save()` aufgerufen wird. Jeder am entfernten Ende der Anwendung könnte die Datenbank jederzeit mit `Article::setDb()` ändern. Unter unseren Händen. + +Der globale Zustand macht unsere Anwendung **extrem anfällig**. + +Es gibt jedoch eine einfache Möglichkeit, mit diesem Problem umzugehen. Lassen Sie die API einfach Abhängigkeiten deklarieren, um die ordnungsgemäße Funktionalität zu gewährleisten. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Auf diese Weise wird die Sorge vor versteckten und unerwarteten Änderungen an Datenbankverbindungen beseitigt. Jetzt wissen wir genau, wo der Artikel gespeichert ist, und keine Code-Änderungen in einer anderen, nicht verwandten Klasse können die Situation mehr verändern. Der Code ist nicht mehr anfällig, sondern stabil. + +Schreiben Sie keinen Code, der globale Zustände verwendet, sondern übergeben Sie lieber Abhängigkeiten. Daher Dependency Injection. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton ist ein Entwurfsmuster, das gemäß der [Definition |https://en.wikipedia.org/wiki/Singleton_pattern] aus der berühmten Gang of Four-Publikation eine Klasse auf eine einzige Instanz beschränkt und globalen Zugriff auf diese bietet. Die Implementierung dieses Musters ähnelt normalerweise dem folgenden Code: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // und andere Methoden, die die Funktionen der Klasse ausführen +} +``` + +Leider führt das Singleton einen globalen Zustand in die Anwendung ein. Und wie wir oben gezeigt haben, ist ein globaler Zustand unerwünscht. Deshalb wird das Singleton als Antipattern betrachtet. + +Verwenden Sie keine Singletons in Ihrem Code und ersetzen Sie sie durch andere Mechanismen. Sie brauchen Singletons wirklich nicht. Wenn Sie jedoch die Existenz einer einzigen Instanz einer Klasse für die gesamte Anwendung garantieren müssen, überlassen Sie dies dem [DI-Container |container]. +Erstellen Sie also ein Anwendungssingleton oder einen Dienst. Dadurch wird die Klasse nicht mehr für ihre eigene Einzigartigkeit sorgen (d. h. sie wird keine `getInstance()` -Methode und keine statische Variable haben) und nur ihre Funktionen ausführen. Damit wird das Prinzip der einzigen Verantwortung nicht mehr verletzt. + + +Globaler Zustand vs. Tests .[#toc-global-state-versus-tests] +------------------------------------------------------------ + +Beim Schreiben von Tests gehen wir davon aus, dass jeder Test eine isolierte Einheit ist und dass kein externer Zustand in ihn eintritt. Und kein Zustand verlässt die Tests. Wenn ein Test abgeschlossen ist, sollte jeder mit dem Test verbundene Zustand automatisch vom Garbage Collector entfernt werden. Dadurch werden die Tests isoliert. Daher können wir die Tests in beliebiger Reihenfolge ausführen. + +Wenn jedoch globale Zustände/Singletons vorhanden sind, sind alle diese schönen Annahmen hinfällig. Ein Zustand kann einen Test betreten und verlassen. Plötzlich kann die Reihenfolge der Tests eine Rolle spielen. + +Um Singletons überhaupt testen zu können, müssen Entwickler oft ihre Eigenschaften lockern, indem sie beispielsweise zulassen, dass eine Instanz durch eine andere ersetzt wird. Solche Lösungen sind bestenfalls Hacks, die schwer zu wartenden und schwer zu verstehenden Code produzieren. Jeder Test oder jede Methode `tearDown()`, die einen globalen Zustand beeinflusst, muss diese Änderungen rückgängig machen. + +Der globale Zustand ist das größte Problem bei Unit-Tests! + +Wie kann man das Problem lösen? Ganz einfach. Schreiben Sie keinen Code, der Singletons verwendet, sondern ziehen Sie es vor, Abhängigkeiten zu übergeben. Das heißt, dependency injection. + + +Globale Konstanten .[#toc-global-constants] +------------------------------------------- + +Der globale Status ist nicht auf die Verwendung von Singletons und statischen Variablen beschränkt, sondern kann auch für globale Konstanten gelten. + +Konstanten, deren Wert uns keine neuen (`M_PI`) oder nützlichen (`PREG_BACKTRACK_LIMIT_ERROR`) Informationen liefert, sind eindeutig in Ordnung. +Umgekehrt sind Konstanten, die dazu dienen, Informationen innerhalb des Codes *drahtlos* weiterzugeben, nichts anderes als eine versteckte Abhängigkeit. Wie `LOG_FILE` im folgenden Beispiel. +Die Verwendung der Konstante `FILE_APPEND` ist völlig korrekt. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +In diesem Fall sollten wir den Parameter im Konstruktor der Klasse `Foo` deklarieren, um ihn zum Bestandteil der API zu machen: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Jetzt können wir Informationen über den Pfad zur Protokollierungsdatei übergeben und ihn bei Bedarf leicht ändern, was das Testen und Warten des Codes erleichtert. + + +Globale Funktionen und statische Methoden .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------------------------- + +Wir möchten betonen, dass die Verwendung von statischen Methoden und globalen Funktionen an sich nicht problematisch ist. Wir haben die Unangemessenheit der Verwendung von `DB::insert()` und ähnlichen Methoden erläutert, aber es ging immer um den globalen Zustand, der in einer statischen Variablen gespeichert wird. Die Methode `DB::insert()` erfordert das Vorhandensein einer statischen Variablen, weil sie die Datenbankverbindung speichert. Ohne diese Variable wäre es unmöglich, die Methode zu implementieren. + +Die Verwendung von deterministischen statischen Methoden und Funktionen, wie `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` und viele andere, ist mit der Dependency Injection vollkommen vereinbar. Diese Funktionen liefern immer die gleichen Ergebnisse für die gleichen Eingabeparameter und sind daher vorhersehbar. Sie verwenden keinen globalen Zustand. + +Allerdings gibt es in PHP Funktionen, die nicht deterministisch sind. Dazu gehört z.B. die Funktion `htmlspecialchars()`. Ihr dritter Parameter, `$encoding`, wird, wenn er nicht angegeben wird, standardmäßig mit dem Wert der Konfigurationsoption `ini_get('default_charset')` belegt. Es wird daher empfohlen, diesen Parameter immer anzugeben, um ein unvorhersehbares Verhalten der Funktion zu vermeiden. Nette tut dies konsequent. + +Einige Funktionen, wie `strtolower()`, `strtoupper()` und ähnliche, haben sich in der jüngsten Vergangenheit nicht deterministisch verhalten und waren von der Einstellung `setlocale()` abhängig. Dies führte zu zahlreichen Komplikationen, vor allem bei der Arbeit mit der türkischen Sprache. +Das liegt daran, dass die türkische Sprache zwischen Groß- und Kleinschreibung `I` mit und ohne Punkt unterscheidet. So gab `strtolower('I')` das Zeichen `ı` und `strtoupper('i')` das Zeichen `İ` zurück, was dazu führte, dass Anwendungen eine Reihe von mysteriösen Fehlern verursachten. +Dieses Problem wurde jedoch in der PHP-Version 8.2 behoben, und die Funktionen sind nun nicht mehr vom Gebietsschema abhängig. + +Dies ist ein schönes Beispiel dafür, wie der globale Zustand Tausende von Entwicklern auf der ganzen Welt geplagt hat. Die Lösung bestand darin, ihn durch Dependency Injection zu ersetzen. + + +Wann ist es möglich, einen globalen Status zu verwenden? .[#toc-when-is-it-possible-to-use-global-state] +-------------------------------------------------------------------------------------------------------- + +Es gibt bestimmte Situationen, in denen es möglich ist, globale Zustände zu verwenden. Zum Beispiel beim Debuggen von Code, wenn Sie den Wert einer Variablen ausgeben oder die Dauer eines bestimmten Programmteils messen müssen. In solchen Fällen, die temporäre Aktionen betreffen, die später aus dem Code entfernt werden, ist es legitim, einen global verfügbaren Dumper oder eine Stoppuhr zu verwenden. Diese Werkzeuge sind nicht Teil des Codeentwurfs. + +Ein weiteres Beispiel sind die Funktionen für die Arbeit mit regulären Ausdrücken `preg_*`, die intern kompilierte reguläre Ausdrücke in einem statischen Cache im Speicher ablegen. Wenn Sie denselben regulären Ausdruck mehrmals in verschiedenen Teilen des Codes aufrufen, wird er nur einmal kompiliert. Der Cache spart Leistung und ist außerdem für den Benutzer völlig unsichtbar, so dass eine solche Verwendung als legitim angesehen werden kann. + + +Zusammenfassung .[#toc-summary] +------------------------------- + +Wir haben gezeigt, warum es Sinn macht + +1) Entfernen Sie alle statischen Variablen aus dem Code +2) Deklarieren Sie Abhängigkeiten +3) Und verwenden Sie Dependency Injection + +Wenn Sie über den Entwurf von Code nachdenken, sollten Sie bedenken, dass jedes `static $foo` ein Problem darstellt. Damit Ihr Code eine DI-konforme Umgebung wird, ist es unerlässlich, den globalen Zustand vollständig zu beseitigen und durch Dependency Injection zu ersetzen. + +Während dieses Prozesses kann es vorkommen, dass Sie eine Klasse aufteilen müssen, weil sie mehr als eine Verantwortung hat. Machen Sie sich keine Gedanken darüber; streben Sie das Prinzip der einen Verantwortung an. + +*Ich möchte Miško Hevery danken, dessen Artikel wie [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] die Grundlage für dieses Kapitel bilden.* diff --git a/dependency-injection/el/@home.texy b/dependency-injection/el/@home.texy index aa5da71fe8..98572fa0d7 100644 --- a/dependency-injection/el/@home.texy +++ b/dependency-injection/el/@home.texy @@ -5,8 +5,9 @@ Το Dependency Injection είναι ένα πρότυπο σχεδίασης που θα αλλάξει ριζικά τον τρόπο με τον οποίο βλέπετε τον κώδικα και την ανάπτυξη. Ανοίγει το δρόμο για έναν κόσμο καθαρά σχεδιασμένων και βιώσιμων εφαρμογών. - [Τι είναι το Dependency Injection; |introduction] -- [Τι είναι το DI Container; |container] +- [Παγκόσμια κατάσταση & Singletons |global-state] - [Πέρασμα εξαρτήσεων |passing-dependencies] +- [Τι είναι το DI Container; |container] Nette DI diff --git a/dependency-injection/el/@left-menu.texy b/dependency-injection/el/@left-menu.texy index 86bd4cabbd..e4ec1244c6 100644 --- a/dependency-injection/el/@left-menu.texy +++ b/dependency-injection/el/@left-menu.texy @@ -1,8 +1,9 @@ Εγχώνευση εξάρτησης ******************* - [Τι είναι η DI; |introduction] -- [Τι είναι το DI Container; |container] +- [Παγκόσμια κατάσταση & Singletons |global-state] - [Πέρασμα εξαρτήσεων |passing-dependencies] +- [Τι είναι το DI Container; |container] Nette DI diff --git a/dependency-injection/el/global-state.texy b/dependency-injection/el/global-state.texy new file mode 100644 index 0000000000..5c6cbcecdf --- /dev/null +++ b/dependency-injection/el/global-state.texy @@ -0,0 +1,312 @@ +Παγκόσμια κατάσταση και singletons +********************************** + +.[perex] +Προειδοποίηση: οι ακόλουθες δομές είναι συμπτώματα κακού σχεδιασμού κώδικα: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` ή `static::$var` + +Εμφανίζεται κάποια από αυτές τις δομές στον κώδικά σας; Τότε έχετε την ευκαιρία να βελτιωθείτε. Μπορεί να σκέφτεστε ότι πρόκειται για κοινές κατασκευές που βλέπουμε σε δείγματα λύσεων διαφόρων βιβλιοθηκών και πλαισίων. +Δυστυχώς, εξακολουθούν να αποτελούν σαφή ένδειξη κακού σχεδιασμού. Έχουν ένα κοινό χαρακτηριστικό: τη χρήση της παγκόσμιας κατάστασης. + +Τώρα, σίγουρα δεν μιλάμε για κάποιο είδος ακαδημαϊκής καθαρότητας. Η χρήση global state και singletons έχει καταστροφικά αποτελέσματα στην ποιότητα του κώδικα. Η συμπεριφορά της γίνεται απρόβλεπτη, μειώνει την παραγωγικότητα των προγραμματιστών και αναγκάζει τις διεπαφές κλάσεων να λένε ψέματα για τις πραγματικές εξαρτήσεις τους. Το οποίο προκαλεί σύγχυση στους προγραμματιστές. + +Σε αυτό το κεφάλαιο, θα δείξουμε πώς αυτό είναι εφικτό. + + +Παγκόσμια διασύνδεση .[#toc-global-interlinking] +------------------------------------------------ + +Το θεμελιώδες πρόβλημα με το παγκόσμιο κράτος είναι ότι είναι παγκοσμίως προσβάσιμο. Αυτό καθιστά δυνατή την εγγραφή στη βάση δεδομένων μέσω της παγκόσμιας (στατικής) μεθόδου `DB::insert()`. +Σε έναν ιδανικό κόσμο, ένα αντικείμενο θα πρέπει να μπορεί να επικοινωνεί μόνο με άλλα αντικείμενα που έχουν [περάσει απευθείας σε |passing-dependencies] αυτό. +Αν δημιουργήσω δύο αντικείμενα `A` και `B` και δεν περάσω ποτέ μια αναφορά από το `A` στο `B`, τότε ούτε το `A`, ούτε το `B` μπορούν να έχουν πρόσβαση στο άλλο αντικείμενο ή να αλλάξουν την κατάστασή του. +Αυτό είναι ένα πολύ επιθυμητό χαρακτηριστικό του κώδικα. Είναι παρόμοιο με το να έχετε μια μπαταρία και μια λάμπα- η λάμπα δεν θα ανάψει μέχρι να τα συνδέσετε μεταξύ τους. + +Αυτό δεν ισχύει για παγκόσμιες (στατικές) μεταβλητές ή singletons. Το αντικείμενο `A` θα μπορούσε να προσπελάσει *ασύρματα* το αντικείμενο `C` και να το τροποποιήσει χωρίς να περάσει καμία αναφορά, καλώντας το `C::changeSomething()`. +Εάν το αντικείμενο `B` αρπάζει επίσης το παγκόσμιο `C`, τότε τα `A` και `B` μπορούν να αλληλεπιδράσουν μεταξύ τους μέσω του `C`. + +Η χρήση των παγκόσμιων μεταβλητών εισάγει μια νέα μορφή *ασύρματης* σύζευξης στο σύστημα που δεν είναι ορατή από έξω. +Δημιουργεί ένα προπέτασμα καπνού που περιπλέκει την κατανόηση και τη χρήση του κώδικα. +Οι προγραμματιστές πρέπει να διαβάσουν κάθε γραμμή του πηγαίου κώδικα για να κατανοήσουν πραγματικά τις εξαρτήσεις. Αντί να εξοικειώνονται απλώς με τη διεπαφή των κλάσεων. +Επιπλέον, πρόκειται για μια εντελώς περιττή σύζευξη. + +.[note] +Όσον αφορά τη συμπεριφορά, δεν υπάρχει καμία διαφορά μεταξύ μιας παγκόσμιας και μιας στατικής μεταβλητής. Είναι εξίσου επιβλαβείς. + + +Η τρομακτική δράση από απόσταση .[#toc-the-spooky-action-at-a-distance] +----------------------------------------------------------------------- + +"Φαινομενική δράση από απόσταση" - έτσι ονόμασε ο Άλμπερτ Αϊνστάιν ένα φαινόμενο της κβαντικής φυσικής που τον ανατρίχιασε το 1935. +Πρόκειται για την κβαντική διεμπλοκή, η ιδιαιτερότητα της οποίας είναι ότι όταν μετράτε πληροφορίες για ένα σωματίδιο, επηρεάζετε αμέσως ένα άλλο σωματίδιο, ακόμη και αν αυτά απέχουν εκατομμύρια έτη φωτός. +γεγονός που φαινομενικά παραβιάζει τον θεμελιώδη νόμο του σύμπαντος ότι τίποτα δεν μπορεί να ταξιδέψει γρηγορότερα από το φως. + +Στον κόσμο του λογισμικού, μπορούμε να ονομάσουμε "spooky action at a distance" μια κατάσταση κατά την οποία εκτελούμε μια διαδικασία που νομίζουμε ότι είναι απομονωμένη (επειδή δεν της έχουμε περάσει καμία αναφορά), αλλά απροσδόκητες αλληλεπιδράσεις και αλλαγές κατάστασης συμβαίνουν σε απομακρυσμένες θέσεις του συστήματος για τις οποίες δεν ενημερώσαμε το αντικείμενο. Αυτό μπορεί να συμβεί μόνο μέσω της παγκόσμιας κατάστασης. + +Φανταστείτε να ενταχθείτε σε μια ομάδα ανάπτυξης έργου που έχει μια μεγάλη, ώριμη βάση κώδικα. Ο νέος σας επικεφαλής σας ζητά να υλοποιήσετε ένα νέο χαρακτηριστικό και, σαν καλός προγραμματιστής, ξεκινάτε γράφοντας μια δοκιμή. Αλλά επειδή είστε νέος στο έργο, κάνετε πολλές διερευνητικές δοκιμές τύπου "τι συμβαίνει αν καλέσω αυτή τη μέθοδο". Και προσπαθείτε να γράψετε την ακόλουθη δοκιμή: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // τον αριθμό της κάρτας σας + $cc->charge(100); +} +``` + +Εκτελείτε τον κώδικα, ίσως αρκετές φορές, και μετά από λίγο παρατηρείτε ειδοποιήσεις στο τηλέφωνό σας από την τράπεζα ότι κάθε φορά που τον εκτελείτε, χρεώνονται 100 δολάρια στην πιστωτική σας κάρτα 🤦‍♂️ + +Πώς στο καλό θα μπορούσε το τεστ να προκαλέσει πραγματική χρέωση; Δεν είναι εύκολο να λειτουργήσει με πιστωτική κάρτα. Πρέπει να αλληλεπιδράσετε με μια διαδικτυακή υπηρεσία τρίτου μέρους, πρέπει να γνωρίζετε τη διεύθυνση URL αυτής της διαδικτυακής υπηρεσίας, πρέπει να συνδεθείτε και ούτω καθεξής. +Καμία από αυτές τις πληροφορίες δεν περιλαμβάνεται στη δοκιμή. Ακόμα χειρότερα, δεν γνωρίζετε καν πού υπάρχουν αυτές οι πληροφορίες και, επομένως, πώς να παριστάνετε τις εξωτερικές εξαρτήσεις, ώστε κάθε εκτέλεση να μην οδηγεί σε νέα χρέωση 100 δολαρίων. Και ως νέος προγραμματιστής, πώς υποτίθεται ότι θα γνωρίζατε ότι αυτό που θα κάνατε θα σας οδηγούσε στο να γίνετε κατά 100 δολάρια φτωχότεροι; + +Αυτή είναι μια τρομακτική δράση από απόσταση! + +Δεν έχετε άλλη επιλογή από το να ψάξετε πολύ πηγαίο κώδικα, ρωτώντας παλαιότερους και πιο έμπειρους συναδέλφους, μέχρι να καταλάβετε πώς λειτουργούν οι συνδέσεις στο έργο. +Αυτό οφείλεται στο γεγονός ότι, όταν εξετάζετε τη διεπαφή της κλάσης `CreditCard`, δεν μπορείτε να προσδιορίσετε την παγκόσμια κατάσταση που πρέπει να αρχικοποιηθεί. Ακόμα και αν κοιτάξετε τον πηγαίο κώδικα της κλάσης δεν θα σας πει ποια μέθοδος αρχικοποίησης πρέπει να καλέσετε. Στην καλύτερη περίπτωση, μπορείτε να βρείτε την παγκόσμια μεταβλητή στην οποία γίνεται πρόσβαση και να προσπαθήσετε να μαντέψετε πώς να την αρχικοποιήσετε από αυτήν. + +Οι κλάσεις σε ένα τέτοιο έργο είναι παθολογικοί ψεύτες. Η κάρτα πληρωμών προσποιείται ότι μπορείτε απλώς να την ενσαρκώσετε και να καλέσετε τη μέθοδο `charge()`. Ωστόσο, κρυφά αλληλεπιδρά με μια άλλη κλάση, την `PaymentGateway`. Ακόμη και η διεπαφή της λέει ότι μπορεί να αρχικοποιηθεί ανεξάρτητα, αλλά στην πραγματικότητα αντλεί διαπιστευτήρια από κάποιο αρχείο ρυθμίσεων κ.ο.κ. +Είναι σαφές στους προγραμματιστές που έγραψαν αυτόν τον κώδικα ότι το `CreditCard` χρειάζεται το `PaymentGateway`. Έγραψαν τον κώδικα με αυτόν τον τρόπο. Αλλά για οποιονδήποτε νέο στο έργο, αυτό είναι ένα πλήρες μυστήριο και εμποδίζει την εκμάθηση. + +Πώς να διορθώσετε την κατάσταση; Εύκολα. **Αφήστε το API να δηλώσει εξαρτήσεις.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Παρατηρήστε πώς οι σχέσεις μέσα στον κώδικα είναι ξαφνικά προφανείς. Δηλώνοντας ότι η μέθοδος `charge()` χρειάζεται τη διεύθυνση `PaymentGateway`, δεν χρειάζεται να ρωτήσετε κανέναν πώς ο κώδικας είναι αλληλοεξαρτώμενος. Ξέρετε ότι πρέπει να δημιουργήσετε μια παρουσία της, και όταν προσπαθείτε να το κάνετε αυτό, πέφτετε πάνω στο γεγονός ότι πρέπει να παρέχετε παραμέτρους πρόσβασης. Χωρίς αυτές, ο κώδικας δεν θα μπορούσε καν να εκτελεστεί. + +Και το πιο σημαντικό, μπορείτε τώρα να μιμηθείτε την πύλη πληρωμών, ώστε να μην χρεώνεστε 100 δολάρια κάθε φορά που εκτελείτε μια δοκιμή. + +Η παγκόσμια κατάσταση προκαλεί στα αντικείμενά σας τη δυνατότητα να έχουν κρυφή πρόσβαση σε πράγματα που δεν έχουν δηλωθεί στα API τους, και ως αποτέλεσμα καθιστά τα API σας παθολογικά ψεύτικα. + +Μπορεί να μην το είχατε σκεφτεί με αυτόν τον τρόπο πριν, αλλά κάθε φορά που χρησιμοποιείτε global state, δημιουργείτε μυστικά ασύρματα κανάλια επικοινωνίας. Η ανατριχιαστική απομακρυσμένη δράση αναγκάζει τους προγραμματιστές να διαβάσουν κάθε γραμμή κώδικα για να κατανοήσουν τις πιθανές αλληλεπιδράσεις, μειώνει την παραγωγικότητα των προγραμματιστών και μπερδεύει τα νέα μέλη της ομάδας. +Αν είστε αυτός που δημιούργησε τον κώδικα, γνωρίζετε τις πραγματικές εξαρτήσεις, αλλά όποιος έρχεται μετά από εσάς είναι άσχετος. + +Μη γράφετε κώδικα που χρησιμοποιεί παγκόσμια κατάσταση, προτιμήστε να μεταβιβάζετε εξαρτήσεις. Δηλαδή, την έγχυση εξαρτήσεων (dependency injection). + + +Η ευθραυστότητα του παγκόσμιου κράτους .[#toc-brittleness-of-the-global-state] +------------------------------------------------------------------------------ + +Σε κώδικα που χρησιμοποιεί παγκόσμια κατάσταση και singletons, δεν είναι ποτέ βέβαιο πότε και από ποιον έχει αλλάξει αυτή η κατάσταση. Αυτός ο κίνδυνος υπάρχει ήδη κατά την αρχικοποίηση. Ο παρακάτω κώδικας υποτίθεται ότι δημιουργεί μια σύνδεση με βάση δεδομένων και αρχικοποιεί την πύλη πληρωμών, αλλά συνεχίζει να πετάει μια εξαίρεση και η εύρεση της αιτίας είναι εξαιρετικά κουραστική: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Πρέπει να ψάξετε λεπτομερώς τον κώδικα για να διαπιστώσετε ότι το αντικείμενο `PaymentGateway` αποκτά ασύρματη πρόσβαση σε άλλα αντικείμενα, ορισμένα από τα οποία απαιτούν σύνδεση με βάση δεδομένων. Έτσι, πρέπει να αρχικοποιήσετε τη βάση δεδομένων πριν από το `PaymentGateway`. Ωστόσο, το προπέτασμα καπνού της παγκόσμιας κατάστασης το κρύβει αυτό από εσάς. Πόσο χρόνο θα γλιτώνατε αν το API κάθε κλάσης δεν έλεγε ψέματα και δεν δήλωνε τις εξαρτήσεις του; + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Ένα παρόμοιο πρόβλημα προκύπτει όταν χρησιμοποιείτε παγκόσμια πρόσβαση σε μια σύνδεση βάσης δεδομένων: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Κατά την κλήση της μεθόδου `save()`, δεν είναι βέβαιο αν έχει ήδη δημιουργηθεί μια σύνδεση βάσης δεδομένων και ποιος είναι υπεύθυνος για τη δημιουργία της. Για παράδειγμα, αν θέλαμε να αλλάξουμε τη σύνδεση της βάσης δεδομένων εν κινήσει, ίσως για λόγους δοκιμών, θα έπρεπε πιθανώς να δημιουργήσουμε πρόσθετες μεθόδους όπως οι `DB::reconnect(...)` ή `DB::reconnectForTest()`. + +Σκεφτείτε ένα παράδειγμα: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Πού μπορούμε να είμαστε σίγουροι ότι η βάση δεδομένων δοκιμής χρησιμοποιείται πραγματικά όταν καλούμε το `$article->save()`; Τι γίνεται αν η μέθοδος `Foo::doSomething()` αλλάξει την παγκόσμια σύνδεση της βάσης δεδομένων; Για να το μάθουμε, θα πρέπει να εξετάσουμε τον πηγαίο κώδικα της κλάσης `Foo` και πιθανώς πολλών άλλων κλάσεων. Ωστόσο, αυτή η προσέγγιση θα έδινε μόνο μια βραχυπρόθεσμη απάντηση, καθώς η κατάσταση μπορεί να αλλάξει στο μέλλον. + +Τι θα γινόταν αν μετακινούσαμε τη σύνδεση με τη βάση δεδομένων σε μια στατική μεταβλητή μέσα στην κλάση `Article`; + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Αυτό δεν αλλάζει τίποτα απολύτως. Το πρόβλημα είναι μια παγκόσμια κατάσταση και δεν έχει σημασία σε ποια κλάση κρύβεται. Σε αυτή την περίπτωση, όπως και στην προηγούμενη, δεν έχουμε καμία ένδειξη για το σε ποια βάση δεδομένων γράφεται όταν καλείται η μέθοδος `$article->save()`. Οποιοσδήποτε στο μακρινό άκρο της εφαρμογής θα μπορούσε να αλλάξει τη βάση δεδομένων ανά πάσα στιγμή χρησιμοποιώντας τη μέθοδο `Article::setDb()`. Κάτω από τα χέρια μας. + +Η παγκόσμια κατάσταση καθιστά την εφαρμογή μας **εξαιρετικά εύθραυστη**. + +Ωστόσο, υπάρχει ένας απλός τρόπος να αντιμετωπίσουμε αυτό το πρόβλημα. Απλά βάλτε το API να δηλώσει εξαρτήσεις για να διασφαλιστεί η σωστή λειτουργικότητα. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Αυτή η προσέγγιση εξαλείφει την ανησυχία για κρυφές και απροσδόκητες αλλαγές στις συνδέσεις βάσης δεδομένων. Τώρα είμαστε σίγουροι για το πού αποθηκεύεται το άρθρο και καμία τροποποίηση κώδικα μέσα σε μια άλλη άσχετη κλάση δεν μπορεί πλέον να αλλάξει την κατάσταση. Ο κώδικας δεν είναι πλέον εύθραυστος, αλλά σταθερός. + +Μη γράφετε κώδικα που χρησιμοποιεί παγκόσμια κατάσταση, προτιμήστε να περνάτε εξαρτήσεις. Έτσι, η έγχυση εξαρτήσεων (dependency injection). + + +Singleton .[#toc-singleton] +--------------------------- + +Το Singleton είναι ένα μοτίβο σχεδίασης που, σύμφωνα με τον [ορισμό |https://en.wikipedia.org/wiki/Singleton_pattern] από τη διάσημη δημοσίευση της Gang of Four, περιορίζει μια κλάση σε μια μοναδική περίπτωση και προσφέρει παγκόσμια πρόσβαση σε αυτήν. Η υλοποίηση αυτού του προτύπου μοιάζει συνήθως με τον ακόλουθο κώδικα: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // και άλλες μεθόδους που εκτελούν τις λειτουργίες της κλάσης +} +``` + +Δυστυχώς, το singleton εισάγει παγκόσμια κατάσταση στην εφαρμογή. Και όπως δείξαμε παραπάνω, η παγκόσμια κατάσταση είναι ανεπιθύμητη. Αυτός είναι ο λόγος για τον οποίο το singleton θεωρείται αντιπρότυπο. + +Μην χρησιμοποιείτε singletons στον κώδικά σας και αντικαταστήστε τα με άλλους μηχανισμούς. Πραγματικά δεν χρειάζεστε singletons. Ωστόσο, αν πρέπει να εγγυηθείτε την ύπαρξη μιας και μόνο περίπτωσης μιας κλάσης για ολόκληρη την εφαρμογή, αφήστε το στο [DI |container] container. +Έτσι, δημιουργήστε ένα singleton της εφαρμογής ή μια υπηρεσία. Αυτό θα σταματήσει την κλάση από το να παρέχει τη δική της μοναδικότητα (δηλαδή, δεν θα έχει μια μέθοδο `getInstance()` και μια στατική μεταβλητή) και θα εκτελεί μόνο τις λειτουργίες της. Έτσι, θα σταματήσει να παραβιάζει την αρχή της ενιαίας ευθύνης. + + +Παγκόσμια κατάσταση έναντι δοκιμών .[#toc-global-state-versus-tests] +-------------------------------------------------------------------- + +Όταν γράφουμε δοκιμές, υποθέτουμε ότι κάθε δοκιμή είναι μια απομονωμένη μονάδα και ότι δεν εισέρχεται σε αυτήν καμία εξωτερική κατάσταση. Και καμία κατάσταση δεν φεύγει από τις δοκιμές. Όταν μια δοκιμή ολοκληρώνεται, κάθε κατάσταση που σχετίζεται με τη δοκιμή θα πρέπει να αφαιρείται αυτόματα από τον garbage collector. Αυτό καθιστά τις δοκιμές απομονωμένες. Επομένως, μπορούμε να εκτελέσουμε τις δοκιμές με οποιαδήποτε σειρά. + +Ωστόσο, αν υπάρχουν καθολικές καταστάσεις/συνθήκες, όλες αυτές οι ωραίες υποθέσεις καταρρέουν. Μια κατάσταση μπορεί να εισέλθει και να εξέλθει από μια δοκιμή. Ξαφνικά, η σειρά των δοκιμών μπορεί να έχει σημασία. + +Για να δοκιμάσουν καθόλου singletons, οι προγραμματιστές πρέπει συχνά να χαλαρώσουν τις ιδιότητές τους, ίσως επιτρέποντας την αντικατάσταση μιας περίπτωσης από μια άλλη. Τέτοιες λύσεις είναι, στην καλύτερη περίπτωση, χάκερς που παράγουν κώδικα που είναι δύσκολο να συντηρηθεί και να κατανοηθεί. Κάθε δοκιμή ή μέθοδος `tearDown()` που επηρεάζει οποιαδήποτε παγκόσμια κατάσταση πρέπει να αναιρεί αυτές τις αλλαγές. + +Η παγκόσμια κατάσταση είναι ο μεγαλύτερος πονοκέφαλος στον έλεγχο μονάδων! + +Πώς να διορθώσετε την κατάσταση; Εύκολα. Μη γράφετε κώδικα που χρησιμοποιεί singletons, προτιμήστε να περνάτε εξαρτήσεις. Δηλαδή, με την έγχυση εξαρτήσεων (dependency injection). + + +Παγκόσμιες σταθερές .[#toc-global-constants] +-------------------------------------------- + +Η παγκόσμια κατάσταση δεν περιορίζεται στη χρήση των singletons και των στατικών μεταβλητών, αλλά μπορεί να εφαρμοστεί και στις παγκόσμιες σταθερές. + +Οι σταθερές των οποίων η τιμή δεν μας παρέχει καμία νέα (`M_PI`) ή χρήσιμη (`PREG_BACKTRACK_LIMIT_ERROR`) πληροφορία είναι σαφώς ΟΚ. +Αντίθετα, οι σταθερές που χρησιμεύουν ως ένας τρόπος για την *ασύρματη* μετάδοση πληροφοριών μέσα στον κώδικα δεν είναι τίποτα περισσότερο από μια κρυφή εξάρτηση. Όπως το `LOG_FILE` στο ακόλουθο παράδειγμα. +Η χρήση της σταθεράς `FILE_APPEND` είναι απολύτως σωστή. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Σε αυτή την περίπτωση, θα πρέπει να δηλώσουμε την παράμετρο στον κατασκευαστή της κλάσης `Foo` για να γίνει μέρος του API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Τώρα μπορούμε να περνάμε πληροφορίες σχετικά με τη διαδρομή του αρχείου καταγραφής και να την αλλάζουμε εύκολα ανάλογα με τις ανάγκες, διευκολύνοντας έτσι τον έλεγχο και τη συντήρηση του κώδικα. + + +Παγκόσμιες συναρτήσεις και στατικές μέθοδοι .[#toc-global-functions-and-static-methods] +--------------------------------------------------------------------------------------- + +Θέλουμε να τονίσουμε ότι η χρήση στατικών μεθόδων και παγκόσμιων συναρτήσεων δεν είναι από μόνη της προβληματική. Έχουμε εξηγήσει την ακαταλληλότητα της χρήσης του `DB::insert()` και παρόμοιων μεθόδων, αλλά πρόκειται πάντα για την παγκόσμια κατάσταση που αποθηκεύεται σε μια στατική μεταβλητή. Η μέθοδος `DB::insert()` απαιτεί την ύπαρξη μιας στατικής μεταβλητής επειδή αποθηκεύει τη σύνδεση με τη βάση δεδομένων. Χωρίς αυτή τη μεταβλητή, θα ήταν αδύνατη η υλοποίηση της μεθόδου. + +Η χρήση ντετερμινιστικών στατικών μεθόδων και συναρτήσεων, όπως οι `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` και πολλές άλλες, είναι απόλυτα συνεπής με την έγχυση εξάρτησης. Αυτές οι συναρτήσεις επιστρέφουν πάντα τα ίδια αποτελέσματα από τις ίδιες παραμέτρους εισόδου και επομένως είναι προβλέψιμες. Δεν χρησιμοποιούν καμία παγκόσμια κατάσταση. + +Ωστόσο, υπάρχουν συναρτήσεις στην PHP που δεν είναι ντετερμινιστικές. Σε αυτές περιλαμβάνεται, για παράδειγμα, η συνάρτηση `htmlspecialchars()`. Η τρίτη της παράμετρος, `$encoding`, αν δεν καθοριστεί, έχει ως προεπιλογή την τιμή της επιλογής διαμόρφωσης `ini_get('default_charset')`. Επομένως, συνιστάται να προσδιορίζετε πάντα αυτή την παράμετρο για να αποφύγετε πιθανή απρόβλεπτη συμπεριφορά της συνάρτησης. Η Nette το πράττει αυτό με συνέπεια. + +Ορισμένες συναρτήσεις, όπως η `strtolower()`, η `strtoupper()`, και οι παρόμοιες, είχαν μη ντετερμινιστική συμπεριφορά στο πρόσφατο παρελθόν και εξαρτιόνταν από τη ρύθμιση `setlocale()`. Αυτό προκάλεσε πολλές επιπλοκές, τις περισσότερες φορές όταν εργάζονταν με την τουρκική γλώσσα. +Αυτό οφείλεται στο γεγονός ότι η τουρκική γλώσσα κάνει διάκριση μεταξύ κεφαλαίων και πεζών `I` με και χωρίς τελεία. Έτσι το `strtolower('I')` επέστρεφε τον χαρακτήρα `ı` και το `strtoupper('i')` επέστρεφε τον χαρακτήρα `İ`, γεγονός που οδηγούσε τις εφαρμογές να προκαλούν μια σειρά από μυστηριώδη σφάλματα. +Ωστόσο, το πρόβλημα αυτό διορθώθηκε στην έκδοση 8.2 της PHP και οι λειτουργίες δεν εξαρτώνται πλέον από την τοπική γλώσσα. + +Αυτό είναι ένα ωραίο παράδειγμα του πώς η παγκόσμια κατάσταση έχει ταλαιπωρήσει χιλιάδες προγραμματιστές σε όλο τον κόσμο. Η λύση ήταν η αντικατάστασή του με την έγχυση εξάρτησης. + + +Πότε είναι δυνατή η χρήση του Global State; .[#toc-when-is-it-possible-to-use-global-state] +------------------------------------------------------------------------------------------- + +Υπάρχουν ορισμένες συγκεκριμένες καταστάσεις στις οποίες είναι δυνατή η χρήση καθολικής κατάστασης. Για παράδειγμα, όταν κάνετε αποσφαλμάτωση κώδικα και πρέπει να απορρίψετε την τιμή μιας μεταβλητής ή να μετρήσετε τη διάρκεια ενός συγκεκριμένου τμήματος του προγράμματος. Σε τέτοιες περιπτώσεις, οι οποίες αφορούν προσωρινές ενέργειες που θα αφαιρεθούν αργότερα από τον κώδικα, είναι θεμιτό να χρησιμοποιήσετε έναν σφαιρικά διαθέσιμο ντάμπερ ή ένα χρονόμετρο. Τα εργαλεία αυτά δεν αποτελούν μέρος του σχεδιασμού του κώδικα. + +Ένα άλλο παράδειγμα είναι οι συναρτήσεις για την εργασία με κανονικές εκφράσεις `preg_*`, οι οποίες αποθηκεύουν εσωτερικά τις μεταγλωττισμένες κανονικές εκφράσεις σε μια στατική κρυφή μνήμη στη μνήμη. Όταν καλείτε την ίδια κανονική έκφραση πολλές φορές σε διαφορετικά μέρη του κώδικα, αυτή μεταγλωττίζεται μόνο μία φορά. Η κρυφή μνήμη εξοικονομεί απόδοση και είναι επίσης εντελώς αόρατη στον χρήστη, οπότε μια τέτοια χρήση μπορεί να θεωρηθεί νόμιμη. + + +Περίληψη .[#toc-summary] +------------------------ + +Δείξαμε γιατί έχει νόημα + +1) Αφαιρέστε όλες τις στατικές μεταβλητές από τον κώδικα +2) Δηλώστε εξαρτήσεις +3) Και χρησιμοποιήστε την έγχυση εξαρτήσεων + +Όταν μελετάτε το σχεδιασμό κώδικα, να έχετε κατά νου ότι κάθε `static $foo` αντιπροσωπεύει ένα πρόβλημα. Προκειμένου ο κώδικάς σας να είναι ένα περιβάλλον που σέβεται το DI, είναι απαραίτητο να εξαλείψετε εντελώς την παγκόσμια κατάσταση και να την αντικαταστήσετε με έγχυση εξαρτήσεων. + +Κατά τη διάρκεια αυτής της διαδικασίας, μπορεί να διαπιστώσετε ότι πρέπει να χωρίσετε μια κλάση επειδή έχει περισσότερες από μία αρμοδιότητες. Μην ανησυχείτε γι' αυτό- επιδιώξτε την αρχή της μίας ευθύνης. + +*Θα ήθελα να ευχαριστήσω τον Miško Hevery, του οποίου άρθρα όπως το [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] αποτελούν τη βάση αυτού του κεφαλαίου.* diff --git a/dependency-injection/en/@home.texy b/dependency-injection/en/@home.texy index 99c96616bb..0c4d150cef 100644 --- a/dependency-injection/en/@home.texy +++ b/dependency-injection/en/@home.texy @@ -5,8 +5,9 @@ Dependency Injection Dependency Injection is a design pattern that will fundamentally change the way you look at code and development. It opens the way to a world of cleanly designed and sustainable applications. - [What is Dependency Injection? |introduction] -- [What is DI Container? |container] +- [Global State & Singletons |global-state] - [Passing Dependencies |passing-dependencies] +- [What is DI Container? |container] Nette DI diff --git a/dependency-injection/en/@left-menu.texy b/dependency-injection/en/@left-menu.texy index 30092630f6..574c9c49f2 100644 --- a/dependency-injection/en/@left-menu.texy +++ b/dependency-injection/en/@left-menu.texy @@ -1,8 +1,9 @@ Dependency Injection ******************** - [What is DI? |introduction] -- [What is DI Container? |container] +- [Global State & Singletons |global-state] - [Passing Dependencies |passing-dependencies] +- [What is DI Container? |container] Nette DI diff --git a/dependency-injection/en/container.texy b/dependency-injection/en/container.texy index 0cdcc31316..2f5e18d979 100644 --- a/dependency-injection/en/container.texy +++ b/dependency-injection/en/container.texy @@ -4,7 +4,7 @@ What Is DI Container? .[perex] Dependency injection container (DIC) is a class that can instantiate and configure objects. -It may surprise you, but in many cases you don't need a dependency injection container to take advantage of dependency injection (DI for short). After all, even in [previous chapter|introduction] we showed specific examples of DI and no container was needed. +It may surprise you, but in many cases you don't need a dependency injection container to take advantage of dependency injection (DI for short). After all, even in [introductory chapter|introduction] we showed specific examples of DI and no container was needed. However, if you need to manage a large number of different objects with many dependencies, a dependency injection container will be really useful. Which is perhaps the case for web applications built on a framework. diff --git a/dependency-injection/en/global-state.texy b/dependency-injection/en/global-state.texy new file mode 100644 index 0000000000..8a73af584c --- /dev/null +++ b/dependency-injection/en/global-state.texy @@ -0,0 +1,312 @@ +Global State and Singletons +*************************** + +.[perex] +Warning: the following constructs are symptoms of poor code design: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` or `static::$var` + +Do any of these constructs occur in your code? Then you have an opportunity to improve. You may be thinking that these are common constructs that we see in sample solutions of various libraries and frameworks. +Unfortunately, they are still a clear indicator of poor design. They have one thing in common: the use of global state. + +Now, we're certainly not talking about some kind of academic purity. The use of global state and singletons has destructive effects on code quality. Its behavior becomes unpredictable, reduces developer productivity, and forces class interfaces to lie about their true dependencies. Which confuses programmers. + +In this chapter, we will show how this is possible. + + +Global Interlinking +------------------- + +The fundamental problem with the global state is that it is globally accessible. This makes it possible to write to the database via the global (static) method `DB::insert()`. +In an ideal world, an object should only be able to communicate with other objects that have been [directly passed to |passing-dependencies] it. +If I create two objects `A` and `B` and never pass a reference from `A` to `B`, then neither `A`, nor `B` can access the other object or change its state. +This is a very desirable feature of the code. It's similar to having a battery and a light bulb; the bulb won't light until you wire them together. + +This is not true for global (static) variables or singletons. The `A` object could *wirelessly* access the `C` object and modify it without passing any reference, by calling `C::changeSomething()`. +If the `B` object also grabs the global `C`, then `A` and `B` can interact with each other via `C`. + +The use of global variables introduces a new form of *wireless* coupling into the system that is not visible from the outside. +It creates a smokescreen complicating the understanding and use of the code. +Developers must read every line of source code to truly understand the dependencies. Instead of just familiarizing themselves with the interface of the classes. +Moreover, it is a completely unnecessary coupling. + +.[note] +In terms of behavior, there is no difference between a global and a static variable. They are equally harmful. + + +The Spooky Action at a Distance +------------------------------- + +"Spooky action at a distance" - that's what Albert Einstein famously called a phenomenon in quantum physics that gave him the creeps in 1935. +It is quantum entanglement, the peculiarity of which is that when you measure information about one particle, you immediately affect another particle, even if they are millions of light years apart. +which seemingly violates the fundamental law of the universe that nothing can travel faster than light. + +In the software world, we can call a "spooky action at a distance" a situation where we run a process that we think is isolated (because we haven't passed it any references), but unexpected interactions and state changes happen in distant locations of the system which we did not tell the object about. This can only happen through the global state. + +Imagine joining a project development team that has a large, mature code base. Your new lead asks you to implement a new feature and, like a good developer, you start by writing a test. But because you're new to the project, you do a lot of exploratory "what happens if I call this method" type tests. And you try to write the following test: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // your card number + $cc->charge(100); +} +``` + +You run the code, maybe several times, and after a while you notice notifications on your phone from the bank that each time you run it, $100 was charged to your credit card 🤦‍♂️ + +How on earth could the test cause an actual charge? It's not easy to operate with credit card. You have to interact with a third party web service, you have to know the URL of that web service, you have to log in, and so on. +None of this information is included in the test. Even worse, you don't even know where this information is present, and therefore how to mock external dependencies so that each run doesn't result in $100 being charged again. And as a new developer, how were you supposed to know that what you were about to do would lead to you being $100 poorer? + +That's a spooky action at a distance! + +You have no choice but to dig through a lot of source code, asking older and more experienced colleagues, until you understand how the connections in the project work. +This is due to the fact that when looking at the interface of the `CreditCard` class, you cannot determine the global state that needs to be initialized. Even looking at the source code of the class won't tell you which initialization method to call. At best, you can find the global variable being accessed and try to guess how to initialize it from that. + +The classes in such a project are pathological liars. The payment card pretends that you can just instantiate it and call the `charge()` method. However, it secretly interacts with another class, `PaymentGateway`. Even its interface says it can be initialized independently, but in reality it pulls credentials from some configuration file and so on. +It is clear to the developers who wrote this code that `CreditCard` needs `PaymentGateway`. They wrote the code this way. But for anyone new to the project, this is a complete mystery and hinders learning. + +How to fix the situation? Easy. **Let the API declare dependencies.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Notice how the relationships within the code are suddenly obvious. By declaring that the `charge()` method needs `PaymentGateway`, you don't have to ask anyone how the code is interdependent. You know you have to create an instance of it, and when you try to do so, you run into the fact that you have to supply access parameters. Without them, the code wouldn't even run. + +And most importantly, you can now mock the payment gateway so you won't be charged $100 every time you run a test. + +The global state causes your objects to be able to secretly access things that aren't declared in their APIs, and as a result makes your APIs pathological liars. + +You may not have thought of it this way before, but whenever you use global state, you're creating secret wireless communication channels. Creepy remote action forces developers to read every line of code to understand potential interactions, reduces developer productivity, and confuses new team members. +If you're the one who created the code, you know the real dependencies, but anyone who comes after you is clueless. + +Don't write code that uses global state, prefer to pass dependencies. That is, dependency injection. + + +Brittleness of the Global State +------------------------------- + +In code that uses global state and singletons, it is never certain when and by whom that state has changed. This risk is already present at initialization. The following code is supposed to create a database connection and initialize the payment gateway, but it keeps throwing an exception and finding the cause is extremely tedious: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +You have to go through the code in detail to find that the `PaymentGateway` object accesses other objects wirelessly, some of which require a database connection. Thus, you must initialize the database before `PaymentGateway`. However, the smokescreen of global state hides this from you. How much time would you save if the API of each class did not lie and declare its dependencies? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +A similar problem arises when using global access to a database connection: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +When calling the `save()` method, it is not certain whether a database connection has already been created and who is responsible for creating it. For example, if we wanted to change the database connection on the fly, perhaps for testing purposes, we would probably have to create additional methods such as `DB::reconnect(...)` or `DB::reconnectForTest()`. + +Consider an example: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Where can we be sure that the test database is really being used when calling `$article->save()`? What if the `Foo::doSomething()` method changed the global database connection? To find out, we would have to examine the source code of the `Foo` class and probably many other classes. However, this approach would provide only a short-term answer, as the situation may change in the future. + +What if we move the database connection to a static variable inside the `Article` class? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +This doesn't change anything at all. The problem is a global state and it doesn't matter which class it hides in. In this case, as in the previous one, we have no clue as to what database is being written to when the `$article->save()` method is called. Anyone on the distant end of the application could change the database at any time using `Article::setDb()`. Under our hands. + +The global state makes our application **extremely fragile**. + +However, there is a simple way to deal with this problem. Just have the API declare dependencies to ensure proper functionality. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +This approach eliminates the worry of hidden and unexpected changes to database connections. Now we are sure where the article is stored and no code modifications inside another unrelated class can change the situation anymore. The code is no longer fragile, but stable. + +Don't write code that uses global state, prefer to pass dependencies. Thus, dependency injection. + + +Singleton +--------- + +Singleton is a design pattern that, by [definition |https://en.wikipedia.org/wiki/Singleton_pattern] from the famous Gang of Four publication, restricts a class to a single instance and offers global access to it. The implementation of this pattern usually resembles the following code: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // and other methods that perform the functions of the class +} +``` + +Unfortunately, the singleton introduces global state into the application. And as we have shown above, global state is undesirable. That's why the singleton is considered an antipattern. + +Don't use singletons in your code and replace them with other mechanisms. You really don't need singletons. However, if you need to guarantee the existence of a single instance of a class for the entire application, leave it to the [DI container |container]. +Thus, create an application singleton, or service. This will stop the class from providing its own uniqueness (i.e., it won't have a `getInstance()` method and a static variable) and will only perform its functions. Thus, it will stop violating the single responsibility principle. + + +Global State Versus Tests +------------------------- + +When writing tests, we assume that each test is an isolated unit and that no external state enters it. And no state leaves the tests. When a test completes, any state associated with the test should be removed automatically by the garbage collector. This makes the tests isolated. Therefore, we can run the tests in any order. + +However, if global states/singletons are present, all these nice assumptions break down. A state can enter and exit a test. Suddenly, the order of the tests may matter. + +To test singletons at all, developers often have to relax their properties, perhaps by allowing an instance to be replaced by another. Such solutions are, at best, hacks that produce code that is difficult to maintain and understand. Any test or method `tearDown()` that affects any global state must undo those changes. + +Global state is the biggest headache in unit testing! + +How to fix the situation? Easy. Don't write code that uses singletons, prefer to pass dependencies. That is, dependency injection. + + +Global Constants +---------------- + +Global state is not limited to the use of singletons and static variables, but can also apply to global constants. + +Constants whose value does not provide us with any new (`M_PI`) or useful (`PREG_BACKTRACK_LIMIT_ERROR`) information are clearly OK. +Conversely, constants that serve as a way to *wirelessly* pass information inside the code are nothing more than a hidden dependency. Like `LOG_FILE` in the following example. +Using the `FILE_APPEND` constant is perfectly correct. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +In this case, we should declare the parameter in the constructor of the `Foo` class to make it part of the API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Now we can pass information about the path to the logging file and easily change it as needed, making it easier to test and maintain the code. + + +Global Functions and Static Methods +----------------------------------- + +We want to emphasize that the use of static methods and global functions is not in itself problematic. We have explained the inappropriateness of using `DB::insert()` and similar methods, but it has always been a matter of global state stored in a static variable. The `DB::insert()` method requires the existence of a static variable because it stores the database connection. Without this variable, it would be impossible to implement the method. + +The use of deterministic static methods and functions, such as `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` and many others, is perfectly consistent with dependency injection. These functions always return the same results from the same input parameters and are therefore predictable. They do not use any global state. + +However, there are functions in PHP that are not deterministic. These include, for example, the `htmlspecialchars()` function. Its third parameter, `$encoding`, if not specified, defaults to the value of the configuration option `ini_get('default_charset')`. Therefore, it is recommended to always specify this parameter to avoid possible unpredictable behavior of the function. Nette consistently does this. + +Some functions, such as `strtolower()`, `strtoupper()`, and the like, have had non-deterministic behavior in the recent past and have depended on the `setlocale()` setting. This caused many complications, most often when working with the Turkish language. +This is because the Turkish language distinguishes between upper and lower case `I` with and without a dot. So `strtolower('I')` returned the `ı` character and `strtoupper('i')` returned the `İ` character , which led to applications causing a number of mysterious errors. +However, this problem was fixed in PHP version 8.2 and the functions are no longer locale dependent. + +This is a nice example of how global state has plagued thousands of developers around the world. The solution was to replace it with dependency injection. + + +When Is It Possible to Use Global State? +---------------------------------------- + +There are certain specific situations where it is possible to use global state. For example, when debugging code and you need to dump the value of a variable or measure the duration of a specific part of the program. In such cases, which concern temporary actions that will be later removed from the code, it is legitimate to use a globally available dumper or stopwatch. These tools are not part of the code design. + +Another example is the functions for working with regular expressions `preg_*`, which internally store compiled regular expressions in a static cache in memory. When you call the same regular expression multiple times in different parts of the code, it is compiled only once. The cache saves performance and is also completely invisible to the user, so such usage can be considered legitimate. + + +Summary +------- + +We've shown why it makes sense + +1) Remove all static variables from the code +2) Declare dependencies +3) And use dependency injection + +When contemplating code design, keep in mind that each `static $foo` represents a problem. In order for your code to be a DI-respecting environment, it is essential to completely eradicate global state and replace it with dependency injection. + +During this process, you may find that you need to split a class because it has more than one responsibility. Don't worry about it; strive for the principle of one responsibility. + +*I would like to thank Miško Hevery, whose articles such as [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] form the basis of this chapter.* diff --git a/dependency-injection/es/@home.texy b/dependency-injection/es/@home.texy index 7c4366df5e..b0780ed47c 100644 --- a/dependency-injection/es/@home.texy +++ b/dependency-injection/es/@home.texy @@ -5,8 +5,9 @@ Inyección de dependencia La inyección de dependencias es un patrón de diseño que cambiará radicalmente tu forma de ver el código y el desarrollo. Abre el camino a un mundo de aplicaciones sostenibles y de diseño limpio. - [¿Qué es la inyección de dependencia? |introduction] -- [¿Qué es un Contenedor DI? |container] +- [Estado Global y Singletons |global-state] - [Pasar Dependencias |passing-dependencies] +- [¿Qué es un Contenedor DI? |container] Nette DI diff --git a/dependency-injection/es/@left-menu.texy b/dependency-injection/es/@left-menu.texy index 48d164c2f3..4229800d1d 100644 --- a/dependency-injection/es/@left-menu.texy +++ b/dependency-injection/es/@left-menu.texy @@ -1,8 +1,9 @@ Inyección de dependencia ************************ - [Qué es el DI? |introduction] -- [Qué es el Contenedor DI? |container] +- [Estado Global y Singletons |global-state] - [Pasar dependencias |passing-dependencies] +- [Qué es el Contenedor DI? |container] Nette DI diff --git a/dependency-injection/es/global-state.texy b/dependency-injection/es/global-state.texy new file mode 100644 index 0000000000..96c56d55fd --- /dev/null +++ b/dependency-injection/es/global-state.texy @@ -0,0 +1,312 @@ +Estado global y Singletons +************************** + +.[perex] +Advertencia: las siguientes construcciones son síntomas de un mal diseño de código: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` o `static::$var` + +¿Alguna de estas construcciones aparece en su código? Entonces tienes una oportunidad para mejorar. Puede que pienses que son construcciones comunes que vemos en soluciones de ejemplo de varias bibliotecas y frameworks. +Por desgracia, siguen siendo un claro indicador de un diseño deficiente. Tienen una cosa en común: el uso de estado global. + +Ahora, ciertamente no estamos hablando de algún tipo de pureza académica. El uso de estado global y singletons tiene efectos destructivos en la calidad del código. Su comportamiento se vuelve impredecible, reduce la productividad de los desarrolladores y obliga a las interfaces de las clases a mentir sobre sus verdaderas dependencias. Lo que confunde a los programadores. + +En este capítulo, mostraremos cómo esto es posible. + + +Interconexión global .[#toc-global-interlinking] +------------------------------------------------ + +El problema fundamental del estado global es que es accesible globalmente. Esto hace posible escribir en la base de datos a través del método global (estático) `DB::insert()`. +En un mundo ideal, un objeto sólo debería poder comunicarse con otros objetos que [le hayan sido pasados directamente |passing-dependencies]. +Si creo dos objetos `A` y `B` y nunca paso una referencia de `A` a `B`, entonces ni `A`, ni `B` pueden acceder al otro objeto o cambiar su estado. +Esta es una característica muy deseable del código. Es similar a tener una pila y una bombilla; la bombilla no se encenderá hasta que las conectes. + +Esto no es cierto para variables globales (estáticas) o singletons. El objeto `A` podría acceder *inalámbricamente* al objeto `C` y modificarlo sin pasar ninguna referencia, llamando a `C::changeSomething()`. +Si el objeto `B` también toma el objeto global `C`, entonces `A` y `B` pueden interactuar entre sí a través de `C`. + +El uso de variables globales introduce una nueva forma de acoplamiento *inalámbrico* en el sistema que no es visible desde el exterior. +Crea una cortina de humo que complica la comprensión y el uso del código. +Los desarrolladores deben leer cada línea del código fuente para comprender realmente las dependencias. En lugar de limitarse a familiarizarse con la interfaz de las clases. +Además, es un acoplamiento completamente innecesario. + +.[note] +En términos de comportamiento, no hay diferencia entre una variable global y una estática. Son igualmente perjudiciales. + + +La espeluznante acción a distancia .[#toc-the-spooky-action-at-a-distance] +-------------------------------------------------------------------------- + +"Espeluznante acción a distancia": así llamó Albert Einstein en 1935 a un fenómeno de la física cuántica que le puso los pelos de punta. +Se trata del entrelazamiento cuántico, cuya peculiaridad es que cuando se mide información sobre una partícula, afecta inmediatamente a otra, aunque estén a millones de años luz de distancia. +Lo que aparentemente viola la ley fundamental del universo de que nada puede viajar más rápido que la luz. + +En el mundo del software, podemos llamar "espeluznante acción a distancia" a una situación en la que ejecutamos un proceso que creemos aislado (porque no le hemos pasado ninguna referencia), pero se producen interacciones inesperadas y cambios de estado en lugares distantes del sistema de los que no hemos informado al objeto. Esto sólo puede ocurrir a través del estado global. + +Imagina que te unes a un equipo de desarrollo de un proyecto que tiene una base de código grande y madura. Tu nuevo jefe te pide que implementes una nueva función y, como buen desarrollador, empiezas escribiendo una prueba. Pero como eres nuevo en el proyecto, haces muchas pruebas exploratorias del tipo "qué pasa si llamo a este método". Y tratas de escribir la siguiente prueba: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // su número de tarjeta + $cc->charge(100); +} +``` + +Ejecutas el código, tal vez varias veces, y después de un tiempo notas notificaciones en tu teléfono del banco que cada vez que lo ejecutas, $100 fueron cargados a tu tarjeta de crédito 🤦‍♂️ + +¿Cómo diablos pudo la prueba causar un cargo real? No es fácil operar con tarjeta de crédito. Tienes que interactuar con un servicio web de terceros, tienes que conocer la URL de ese servicio web, tienes que iniciar sesión, etc. +Ninguna de estas informaciones se incluye en la prueba. Peor aún, ni siquiera sabes dónde está presente esta información y, por lo tanto, cómo simular las dependencias externas para que cada ejecución no suponga un nuevo cargo de 100 dólares. Y como nuevo desarrollador, ¿cómo ibas a saber que lo que estabas a punto de hacer te llevaría a ser 100 dólares más pobre? + +¡Eso es una acción espeluznante a distancia! + +No te queda más remedio que escarbar en un montón de código fuente, preguntando a colegas más veteranos y experimentados, hasta que entiendes cómo funcionan las conexiones en el proyecto. +Esto se debe al hecho de que al mirar la interfaz de la clase `CreditCard`, no puedes determinar el estado global que necesita ser inicializado. Incluso mirando el código fuente de la clase no le dirá qué método de inicialización para llamar. Como mucho, puedes encontrar la variable global a la que se accede e intentar adivinar cómo inicializarla a partir de ahí. + +Las clases de un proyecto así son mentirosas patológicas. La tarjeta de pago finge que puedes simplemente instanciarla y llamar al método `charge()`. Sin embargo, secretamente interactúa con otra clase, `PaymentGateway`. Incluso su interfaz dice que se puede inicializar de forma independiente, pero en realidad extrae credenciales de algún archivo de configuración y demás. +Está claro para los desarrolladores que escribieron este código que `CreditCard` necesita a `PaymentGateway`. Ellos escribieron el código de esta manera. Pero para cualquiera que sea nuevo en el proyecto, esto es un completo misterio y dificulta el aprendizaje. + +¿Cómo arreglar la situación? Fácil. **Deja que la API declare las dependencias.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Observa cómo las relaciones dentro del código son repentinamente obvias. Al declarar que el método `charge()` necesita `PaymentGateway`, no tienes que preguntar a nadie cómo el código es interdependiente. Sabes que tienes que crear una instancia del mismo, y cuando intentas hacerlo, te encuentras con el hecho de que tienes que suministrar parámetros de acceso. Sin ellos, el código ni siquiera se ejecutaría. + +Y lo más importante, ahora puedes simular la pasarela de pago para que no te cobren 100 dólares cada vez que ejecutes una prueba. + +El estado global hace que tus objetos puedan acceder secretamente a cosas que no están declaradas en sus APIs, y como resultado hace que tus APIs sean mentirosas patológicas. + +Puede que no lo hayas pensado así antes, pero siempre que usas estado global, estás creando canales secretos de comunicación inalámbrica. La espeluznante acción remota obliga a los desarrolladores a leer cada línea de código para entender las posibles interacciones, reduce la productividad de los desarrolladores y confunde a los nuevos miembros del equipo. +Si eres tú quien ha creado el código, conoces las dependencias reales, pero cualquiera que venga después no tiene ni idea. + +No escribas código que utilice estado global, prefiere pasar dependencias. Es decir, inyección de dependencias. + + +La fragilidad del Estado mundial .[#toc-brittleness-of-the-global-state] +------------------------------------------------------------------------ + +En código que utiliza estado global y singletons, nunca se sabe con certeza cuándo y por quién ha cambiado ese estado. Este riesgo ya está presente en la inicialización. El siguiente código se supone que debe crear una conexión a la base de datos e inicializar la pasarela de pago, pero sigue lanzando una excepción y encontrar la causa es extremadamente tedioso: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Hay que revisar el código en detalle para descubrir que el objeto `PaymentGateway` accede a otros objetos de forma inalámbrica, algunos de los cuales requieren una conexión a la base de datos. Así, debe inicializar la base de datos antes de `PaymentGateway`. Sin embargo, la cortina de humo del estado global te lo oculta. ¿Cuánto tiempo ahorrarías si la API de cada clase no mintiera y declarara sus dependencias? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Un problema similar surge cuando se utiliza el acceso global a una conexión de base de datos: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Cuando se llama al método `save()`, no se sabe con certeza si ya se ha creado una conexión a la base de datos y quién es el responsable de crearla. Por ejemplo, si quisiéramos cambiar la conexión a la base de datos sobre la marcha, quizás con fines de prueba, probablemente tendríamos que crear métodos adicionales como `DB::reconnect(...)` o `DB::reconnectForTest()`. + +Veamos un ejemplo: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +¿Dónde podemos estar seguros de que se está utilizando realmente la base de datos de prueba cuando se llama a `$article->save()`? ¿Qué pasaría si el método `Foo::doSomething()` cambiara la conexión global a la base de datos? Para averiguarlo, tendríamos que examinar el código fuente de la clase `Foo` y probablemente de muchas otras clases. Sin embargo, este enfoque sólo proporcionaría una respuesta a corto plazo, ya que la situación podría cambiar en el futuro. + +¿Y si trasladamos la conexión a la base de datos a una variable estática dentro de la clase `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Esto no cambia nada en absoluto. El problema es un estado global y no importa en qué clase se esconda. En este caso, como en el anterior, no tenemos ni idea de en qué base de datos se está escribiendo cuando se llama al método `$article->save()`. Cualquiera en el extremo distante de la aplicación podría cambiar la base de datos en cualquier momento usando `Article::setDb()`. Bajo nuestras manos. + +El estado global hace que nuestra aplicación sea **extremadamente frágil**. + +Sin embargo, hay una forma sencilla de lidiar con este problema. Basta con hacer que la API declare dependencias para garantizar una funcionalidad adecuada. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Este enfoque elimina la preocupación de cambios ocultos e inesperados en las conexiones a la base de datos. Ahora estamos seguros de dónde se almacena el artículo y ninguna modificación de código dentro de otra clase no relacionada puede cambiar la situación nunca más. El código ya no es frágil, sino estable. + +No escribas código que use estado global, prefiere pasar dependencias. Por lo tanto, la inyección de dependencia. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton es un patrón de diseño que, por [definición |https://en.wikipedia.org/wiki/Singleton_pattern] de la famosa publicación Gang of Four, restringe una clase a una única instancia y ofrece acceso global a la misma. La implementación de este patrón suele parecerse al siguiente código: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // y otros métodos que realizan las funciones de la clase +} +``` + +Desafortunadamente, el singleton introduce estado global en la aplicación. Y como hemos demostrado anteriormente, el estado global no es deseable. Por eso el singleton se considera un antipatrón. + +No utilices singletons en tu código y sustitúyelos por otros mecanismos. Realmente no necesitas singletons. Sin embargo, si necesitas garantizar la existencia de una única instancia de una clase para toda la aplicación, déjalo en manos del [contenedor DI |container]. +Por lo tanto, cree un singleton de aplicación, o servicio. Esto evitará que la clase proporcione su propia unicidad (es decir, no tendrá un método `getInstance()` y una variable estática) y sólo realizará sus funciones. Así, dejará de violar el principio de responsabilidad única. + + +Estado global frente a pruebas .[#toc-global-state-versus-tests] +---------------------------------------------------------------- + +Cuando escribimos pruebas, asumimos que cada prueba es una unidad aislada y que ningún estado externo entra en ella. Y ningún estado sale de las pruebas. Cuando una prueba se completa, cualquier estado asociado con la prueba debe ser eliminado automáticamente por el recolector de basura. Esto hace que las pruebas estén aisladas. Por lo tanto, podemos ejecutar las pruebas en cualquier orden. + +Sin embargo, si hay estados/singletons globales, todas estas suposiciones se vienen abajo. Un estado puede entrar y salir de una prueba. De repente, el orden de las pruebas puede ser importante. + +Para probar los singletons, los desarrolladores a menudo tienen que relajar sus propiedades, tal vez permitiendo que una instancia sea sustituida por otra. Estas soluciones son, en el mejor de los casos, trucos que producen un código difícil de mantener y comprender. Cualquier prueba o método `tearDown()` que afecte a cualquier estado global debe deshacer esos cambios. + +El estado global es el mayor dolor de cabeza en las pruebas unitarias. + +¿Cómo arreglar la situación? Fácil. No escribas código que utilice singletons, prefiere pasar dependencias. Es decir, inyección de dependencias. + + +Constantes globales .[#toc-global-constants] +-------------------------------------------- + +El estado global no se limita al uso de singletons y variables estáticas, sino que también puede aplicarse a las constantes globales. + +Las constantes cuyo valor no nos proporciona ninguna información nueva (`M_PI`) o útil (`PREG_BACKTRACK_LIMIT_ERROR`) están claramente bien. +Por el contrario, las constantes que sirven para pasar información de forma *inalámbrica* dentro del código no son más que una dependencia oculta. Como `LOG_FILE` en el siguiente ejemplo. +Utilizar la constante `FILE_APPEND` es perfectamente correcto. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +En este caso, debemos declarar el parámetro en el constructor de la clase `Foo` para que forme parte de la API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Ahora podemos pasar información sobre la ruta al archivo de registro y cambiarla fácilmente según sea necesario, lo que facilita las pruebas y el mantenimiento del código. + + +Funciones globales y métodos estáticos .[#toc-global-functions-and-static-methods] +---------------------------------------------------------------------------------- + +Queremos enfatizar que el uso de métodos estáticos y funciones globales no es problemático en sí mismo. Hemos explicado lo inapropiado de usar `DB::insert()` y métodos similares, pero siempre ha sido una cuestión de estado global almacenado en una variable estática. El método `DB::insert()` requiere la existencia de una variable estática porque almacena la conexión a la base de datos. Sin esta variable, sería imposible implementar el método. + +El uso de métodos y funciones estáticas deterministas, como `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` y muchos otros, es perfectamente coherente con la inyección de dependencias. Estas funciones siempre devuelven los mismos resultados a partir de los mismos parámetros de entrada y, por lo tanto, son predecibles. No utilizan ningún estado global. + +Sin embargo, hay funciones en PHP que no son deterministas. Estas incluyen, por ejemplo, la función `htmlspecialchars()`. Su tercer parámetro, `$encoding`, si no se especifica, toma por defecto el valor de la opción de configuración `ini_get('default_charset')`. Por lo tanto, se recomienda especificar siempre este parámetro para evitar un posible comportamiento impredecible de la función. Nette lo hace sistemáticamente. + +Algunas funciones, como `strtolower()`, `strtoupper()`, y similares, han tenido un comportamiento no determinista en el pasado reciente y han dependido del parámetro `setlocale()`. Esto causaba muchas complicaciones, sobre todo cuando se trabajaba con el idioma turco. +Esto se debe a que el idioma turco distingue entre mayúsculas y minúsculas `I` con y sin punto. Así que `strtolower('I')` devolvía el carácter `ı` y `strtoupper('i')` devolvía el carácter `İ`, lo que provocaba en las aplicaciones una serie de misteriosos errores. +Sin embargo, este problema se solucionó en la versión 8.2 de PHP y las funciones ya no dependen de la configuración regional. + +Este es un buen ejemplo de cómo el estado global ha plagado a miles de desarrolladores en todo el mundo. La solución fue reemplazarlo con inyección de dependencia. + + +¿Cuándo es posible utilizar el Estado Global? .[#toc-when-is-it-possible-to-use-global-state] +--------------------------------------------------------------------------------------------- + +Hay ciertas situaciones específicas en las que es posible utilizar el estado global. Por ejemplo, cuando se depura código y se necesita volcar el valor de una variable o medir la duración de una parte concreta del programa. En estos casos, que se refieren a acciones temporales que más tarde se eliminarán del código, es legítimo utilizar un dumper o un cronómetro disponibles globalmente. Estas herramientas no forman parte del diseño del código. + +Otro ejemplo son las funciones para trabajar con expresiones regulares `preg_*`, que almacenan internamente expresiones regulares compiladas en una caché estática en memoria. Cuando se llama a la misma expresión regular varias veces en distintas partes del código, sólo se compila una vez. La caché ahorra rendimiento y además es completamente invisible para el usuario, por lo que este uso puede considerarse legítimo. + + +Resumen .[#toc-summary] +----------------------- + +Hemos demostrado por qué tiene sentido + +1) Eliminar todas las variables estáticas del código +2) Declarar dependencias +3) Y utilizar la inyección de dependencias + +Cuando contemples el diseño del código, ten en cuenta que cada `static $foo` representa un problema. Para que tu código sea un entorno respetuoso con DI, es esencial erradicar por completo el estado global y sustituirlo por la inyección de dependencias. + +Durante este proceso, puede que descubras que necesitas dividir una clase porque tiene más de una responsabilidad. No te preocupes por ello; esfuérzate por el principio de una sola responsabilidad. + +*Me gustaría dar las gracias a Miško Hevery, cuyos artículos como [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] forman la base de este capítulo.* diff --git a/dependency-injection/fr/@home.texy b/dependency-injection/fr/@home.texy index 492b692599..e7556da5a6 100644 --- a/dependency-injection/fr/@home.texy +++ b/dependency-injection/fr/@home.texy @@ -5,8 +5,9 @@ Injection de dépendances L'injection de dépendances est un modèle de conception qui va fondamentalement changer votre façon de voir le code et le développement. Il ouvre la voie à un monde d'applications propres et durables. - [Qu'est-ce que l'injection de dépendances ? |introduction] -- [Qu'est-ce qu'un conteneur DI ? |container] +- [État global et singletons |global-state] - [Passage des dépendances |passing-dependencies] +- [Qu'est-ce qu'un conteneur DI ? |container] Nette DI diff --git a/dependency-injection/fr/@left-menu.texy b/dependency-injection/fr/@left-menu.texy index 00b687e6b6..6e7dd7c82f 100644 --- a/dependency-injection/fr/@left-menu.texy +++ b/dependency-injection/fr/@left-menu.texy @@ -1,8 +1,9 @@ Injection de dépendances ************************ - [Qu'est-ce que DI ? |introduction] -- [Qu'est-ce que DI Container ? |container] +- [Etat global et singletons |global-state] - [Passer les dépendances |passing-dependencies] +- [Qu'est-ce que DI Container ? |container] Nette DI diff --git a/dependency-injection/fr/global-state.texy b/dependency-injection/fr/global-state.texy new file mode 100644 index 0000000000..4adc68cf63 --- /dev/null +++ b/dependency-injection/fr/global-state.texy @@ -0,0 +1,312 @@ +État global et singletons +************************* + +.[perex] +Avertissement : les constructions suivantes sont des symptômes d'une mauvaise conception du code : + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` ou `static::$var` + +L'une de ces constructions apparaît-elle dans votre code ? Alors vous avez la possibilité de vous améliorer. Vous pensez peut-être qu'il s'agit de constructions courantes que nous voyons dans des exemples de solutions de diverses bibliothèques et de divers frameworks. +Malheureusement, elles sont toujours un indicateur clair d'une mauvaise conception. Elles ont une chose en commun : l'utilisation d'un état global. + +Il ne s'agit certainement pas d'une sorte de pureté académique. L'utilisation de l'état global et des singletons a des effets destructeurs sur la qualité du code. Son comportement devient imprévisible, réduit la productivité des développeurs et oblige les interfaces de classe à mentir sur leurs véritables dépendances. Ce qui désoriente les programmeurs. + +Dans ce chapitre, nous allons montrer comment cela est possible. + + +Interconnexion globale .[#toc-global-interlinking] +-------------------------------------------------- + +Le problème fondamental de l'état global est qu'il est accessible globalement. Il est donc possible d'écrire dans la base de données via la méthode globale (statique) `DB::insert()`. +Dans un monde idéal, un objet ne devrait pouvoir communiquer qu'avec les autres objets qui lui ont été [directement transmis |passing-dependencies]. +Si je crée deux objets `A` et `B` et que je ne passe jamais une référence de `A` à `B`, ni `A`, ni `B` ne peuvent accéder à l'autre objet ou modifier son état. +Il s'agit d'une caractéristique très souhaitable du code. C'est comme si vous aviez une batterie et une ampoule ; l'ampoule ne s'allume pas tant que vous ne les connectez pas ensemble. + +Ce n'est pas vrai pour les variables globales (statiques) ou les singletons. L'objet `A` pourrait accéder *sans fil* à l'objet `C` et le modifier sans passer de référence, en appelant `C::changeSomething()`. +Si l'objet `B` s'empare également de la variable globale `C`, alors `A` et `B` peuvent interagir entre eux via `C`. + +L'utilisation de variables globales introduit dans le système une nouvelle forme de couplage *sans fil* qui n'est pas visible de l'extérieur. +Elle crée un écran de fumée qui complique la compréhension et l'utilisation du code. +Les développeurs doivent lire chaque ligne du code source pour vraiment comprendre les dépendances. Au lieu de se contenter de se familiariser avec l'interface des classes. +De plus, il s'agit d'un couplage totalement inutile. + +.[note] +En termes de comportement, il n'y a pas de différence entre une variable globale et une variable statique. Elles sont tout aussi nuisibles l'une que l'autre. + + +L'action sinistre à distance .[#toc-the-spooky-action-at-a-distance] +-------------------------------------------------------------------- + +"L'action sinistre à distance" : c'est ainsi qu'Albert Einstein a appelé un phénomène de la physique quantique qui lui a donné la chair de poule en 1935. +Il s'agit de l'intrication quantique, dont la particularité est que lorsque vous mesurez une information sur une particule, vous affectez immédiatement une autre particule, même si elles sont distantes de millions d'années-lumière. +Ce qui semble violer la loi fondamentale de l'univers selon laquelle rien ne peut voyager plus vite que la lumière. + +Dans le monde des logiciels, nous pouvons parler d'une "action étrange à distance", une situation dans laquelle nous exécutons un processus que nous pensons isolé (parce que nous ne lui avons transmis aucune référence), mais des interactions inattendues et des changements d'état se produisent dans des endroits éloignés du système dont nous n'avons pas parlé à l'objet. Cela ne peut se produire qu'à travers l'état global. + +Imaginez que vous rejoignez une équipe de développement de projet qui dispose d'une base de code importante et mature. Votre nouveau chef vous demande d'implémenter une nouvelle fonctionnalité et, comme tout bon développeur, vous commencez par écrire un test. Mais comme vous êtes nouveau dans le projet, vous faites beaucoup de tests exploratoires du type "que se passe-t-il si j'appelle cette méthode". Et vous essayez d'écrire le test suivant : + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // votre numéro de carte + $cc->charge(100); +} +``` + +Vous exécutez le code, peut-être plusieurs fois, et après un certain temps, vous remarquez sur votre téléphone des notifications de la banque indiquant qu'à chaque fois que vous l'exécutez, 100 $ ont été débités sur votre carte de crédit 🤦‍♂️. + +Comment diable le test a-t-il pu provoquer un débit réel ? Il n'est pas facile d'opérer avec une carte de crédit. Vous devez interagir avec un service web tiers, vous devez connaître l'URL de ce service web, vous devez vous connecter, et ainsi de suite. +Aucune de ces informations n'est incluse dans le test. Pire encore, vous ne savez même pas où ces informations sont présentes, et donc comment simuler les dépendances externes pour que chaque exécution n'entraîne pas une nouvelle facturation de 100 dollars. Et en tant que nouveau développeur, comment étiez-vous censé savoir que ce que vous étiez sur le point de faire vous ferait perdre 100 dollars ? + +C'est une action effrayante à distance ! + +Vous n'avez pas d'autre choix que de fouiller dans une grande quantité de code source, en demandant à des collègues plus anciens et plus expérimentés, jusqu'à ce que vous compreniez comment fonctionnent les connexions dans le projet. +Cela est dû au fait qu'en regardant l'interface de la classe `CreditCard`, vous ne pouvez pas déterminer l'état global qui doit être initialisé. Même en regardant le code source de la classe, vous ne pourrez pas savoir quelle méthode d'initialisation appeler. Au mieux, vous pouvez trouver la variable globale à laquelle on accède et essayer de deviner comment l'initialiser à partir de là. + +Les classes d'un tel projet sont des menteurs pathologiques. La carte de paiement prétend que vous pouvez simplement l'instancier et appeler la méthode `charge()`. Cependant, elle interagit secrètement avec une autre classe, `PaymentGateway`. Même son interface indique qu'elle peut être initialisée de manière indépendante, mais en réalité, elle tire les informations d'identification d'un fichier de configuration et ainsi de suite. +Il est clair pour les développeurs qui ont écrit ce code que `CreditCard` a besoin de `PaymentGateway`. Ils ont écrit le code de cette façon. Mais pour toute personne nouvelle dans le projet, c'est un mystère complet et cela entrave l'apprentissage. + +Comment remédier à cette situation ? Facile. **Laissez l'API déclarer les dépendances.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Remarquez comment les relations au sein du code deviennent soudainement évidentes. En déclarant que la méthode `charge()` a besoin de `PaymentGateway`, vous n'avez pas besoin de demander à qui que ce soit comment le code est interdépendant. Vous savez que vous devez créer une instance de cette méthode, et lorsque vous essayez de le faire, vous vous heurtez au fait que vous devez fournir des paramètres d'accès. Sans eux, le code ne fonctionnerait même pas. + +Et surtout, vous pouvez maintenant simuler la passerelle de paiement afin de ne pas être facturé 100 $ à chaque fois que vous exécutez un test. + +L'état global permet à vos objets d'accéder secrètement à des éléments qui ne sont pas déclarés dans leurs API et, par conséquent, fait de vos API des menteurs pathologiques. + +Vous n'y avez peut-être jamais pensé de cette façon, mais chaque fois que vous utilisez l'état global, vous créez des canaux de communication sans fil secrets. Les actions à distance effrayantes obligent les développeurs à lire chaque ligne de code pour comprendre les interactions potentielles, réduisent la productivité des développeurs et désorientent les nouveaux membres de l'équipe. +Si vous êtes celui qui a créé le code, vous connaissez les véritables dépendances, mais tous ceux qui viennent après vous ne savent rien. + +N'écrivez pas de code qui utilise l'état global, préférez passer les dépendances. C'est l'injection de dépendances. + + +La fragilité de l'État mondial .[#toc-brittleness-of-the-global-state] +---------------------------------------------------------------------- + +Dans le code qui utilise un état global et des singletons, on ne sait jamais avec certitude quand et par qui cet état a été modifié. Ce risque est déjà présent à l'initialisation. Le code suivant est censé créer une connexion à une base de données et initialiser la passerelle de paiement, mais il continue à lancer une exception et il est extrêmement fastidieux d'en trouver la cause : + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Vous devez parcourir le code en détail pour découvrir que l'objet `PaymentGateway` accède à d'autres objets sans fil, dont certains nécessitent une connexion à une base de données. Ainsi, vous devez initialiser la base de données avant `PaymentGateway`. Cependant, l'écran de fumée de l'état global vous cache cela. Combien de temps gagneriez-vous si l'API de chaque classe ne mentait pas et ne déclarait pas ses dépendances ? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Un problème similaire se pose lors de l'utilisation de l'accès global à une connexion de base de données : + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Lorsque l'on appelle la méthode `save()`, on ne sait pas si une connexion à la base de données a déjà été créée et qui est responsable de sa création. Par exemple, si nous voulions modifier la connexion à la base de données à la volée, peut-être à des fins de test, nous devrions probablement créer des méthodes supplémentaires telles que `DB::reconnect(...)` ou `DB::reconnectForTest()`. + +Prenons un exemple : + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Comment pouvons-nous être sûrs que la base de données de test est réellement utilisée lors de l'appel à `$article->save()`? Et si la méthode `Foo::doSomething()` modifiait la connexion globale à la base de données ? Pour le savoir, nous devrions examiner le code source de la classe `Foo` et probablement de nombreuses autres classes. Toutefois, cette approche ne fournirait qu'une réponse à court terme, car la situation pourrait changer à l'avenir. + +Et si nous déplacions la connexion à la base de données vers une variable statique à l'intérieur de la classe `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Cela ne change rien du tout. Le problème est un état global et la classe dans laquelle il se cache n'a pas d'importance. Dans ce cas, comme dans le précédent, nous n'avons aucune idée de la base de données qui est écrite lorsque la méthode `$article->save()` est appelée. N'importe qui à l'extrémité distante de l'application pourrait changer la base de données à tout moment en utilisant `Article::setDb()`. Sous nos yeux. + +L'état global rend notre application **extrêmement fragile**. + +Cependant, il existe un moyen simple de résoudre ce problème. Il suffit de faire en sorte que l'API déclare des dépendances pour garantir une bonne fonctionnalité. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Cette approche élimine le souci de modifications cachées et inattendues des connexions à la base de données. Maintenant, nous sommes sûrs de l'endroit où l'article est stocké et aucune modification du code dans une autre classe sans rapport ne peut plus changer la situation. Le code n'est plus fragile, mais stable. + +N'écrivez pas de code qui utilise l'état global, préférez le passage des dépendances. Ainsi, l'injection de dépendances. + + +Singleton .[#toc-singleton] +--------------------------- + +Le singleton est un modèle de conception qui, selon la [définition de |https://en.wikipedia.org/wiki/Singleton_pattern] la célèbre publication Gang of Four, limite une classe à une seule instance et lui offre un accès global. L'implémentation de ce patron ressemble généralement au code suivant : + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // et d'autres méthodes qui exécutent les fonctions de la classe +} +``` + +Malheureusement, le singleton introduit un état global dans l'application. Et comme nous l'avons montré ci-dessus, l'état global n'est pas souhaitable. C'est pourquoi le singleton est considéré comme un anti-modèle. + +N'utilisez pas les singletons dans votre code et remplacez-les par d'autres mécanismes. Vous n'avez vraiment pas besoin de singletons. Toutefois, si vous devez garantir l'existence d'une seule instance d'une classe pour l'ensemble de l'application, confiez cette tâche au [conteneur DI |container]. +Ainsi, créez un singleton d'application, ou service. Cela empêchera la classe de fournir sa propre unicité (c'est-à-dire qu'elle n'aura pas de méthode `getInstance()` et de variable statique) et n'exécutera que ses fonctions. Ainsi, elle cessera de violer le principe de responsabilité unique. + + +État global versus tests .[#toc-global-state-versus-tests] +---------------------------------------------------------- + +Lorsque nous écrivons des tests, nous supposons que chaque test est une unité isolée et qu'aucun état externe n'y entre. Et aucun état ne quitte les tests. Lorsqu'un test se termine, tout état associé au test devrait être supprimé automatiquement par le ramasse-miettes. Cela rend les tests isolés. Par conséquent, nous pouvons exécuter les tests dans n'importe quel ordre. + +Cependant, si des états/singletons globaux sont présents, toutes ces belles hypothèses s'effondrent. Un état peut entrer et sortir d'un test. Soudainement, l'ordre des tests peut avoir de l'importance. + +Pour tester les singletons, les développeurs doivent souvent assouplir leurs propriétés, peut-être en permettant à une instance d'être remplacée par une autre. De telles solutions sont, au mieux, des bidouillages qui produisent un code difficile à maintenir et à comprendre. Tout test ou méthode `tearDown()` qui affecte un état global doit annuler ces changements. + +L'état global est le plus gros casse-tête des tests unitaires ! + +Comment remédier à cette situation ? Facile. N'écrivez pas de code qui utilise des singletons, préférez passer des dépendances. C'est-à-dire l'injection de dépendances. + + +Constantes globales .[#toc-global-constants] +-------------------------------------------- + +L'état global ne se limite pas à l'utilisation des singletons et des variables statiques, mais peut également s'appliquer aux constantes globales. + +Les constantes dont la valeur ne nous fournit aucune information nouvelle (`M_PI`) ou utile (`PREG_BACKTRACK_LIMIT_ERROR`) sont clairement OK. +À l'inverse, les constantes qui servent à faire passer des informations *sans fil* à l'intérieur du code ne sont rien d'autre qu'une dépendance cachée. Comme `LOG_FILE` dans l'exemple suivant. +L'utilisation de la constante `FILE_APPEND` est parfaitement correcte. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Dans ce cas, nous devons déclarer le paramètre dans le constructeur de la classe `Foo` pour qu'il fasse partie de l'API : + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Nous pouvons maintenant transmettre des informations sur le chemin d'accès au fichier de journalisation et le modifier facilement si nécessaire, ce qui facilite les tests et la maintenance du code. + + +Fonctions globales et méthodes statiques .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------------------------ + +Nous tenons à souligner que l'utilisation de méthodes statiques et de fonctions globales n'est pas problématique en soi. Nous avons expliqué le caractère inapproprié de l'utilisation de `DB::insert()` et de méthodes similaires, mais il s'agissait toujours d'un état global stocké dans une variable statique. La méthode `DB::insert()` nécessite l'existence d'une variable statique car elle stocke la connexion à la base de données. Sans cette variable, il serait impossible de mettre en œuvre la méthode. + +L'utilisation de méthodes et de fonctions statiques déterministes, telles que `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` et bien d'autres, est parfaitement compatible avec l'injection de dépendances. Ces fonctions renvoient toujours les mêmes résultats à partir des mêmes paramètres d'entrée et sont donc prévisibles. Elles n'utilisent pas d'état global. + +Cependant, il existe des fonctions en PHP qui ne sont pas déterministes. C'est le cas, par exemple, de la fonction `htmlspecialchars()`. Son troisième paramètre, `$encoding`, s'il n'est pas spécifié, prend par défaut la valeur de l'option de configuration `ini_get('default_charset')`. Il est donc recommandé de toujours spécifier ce paramètre pour éviter un éventuel comportement imprévisible de la fonction. C'est ce que fait systématiquement Nette. + +Certaines fonctions, telles que `strtolower()`, `strtoupper()`, et autres, ont eu un comportement non déterministe dans un passé récent et ont dépendu du paramètre `setlocale()`. Cela a causé de nombreuses complications, le plus souvent en travaillant avec la langue turque. +En effet, la langue turque fait la distinction entre les majuscules et les minuscules `I` avec et sans point. Ainsi, `strtolower('I')` renvoyait le caractère `ı` et `strtoupper('i')` renvoyait le caractère `İ`, ce qui conduisait les applications à provoquer un certain nombre d'erreurs mystérieuses. +Toutefois, ce problème a été corrigé dans la version 8.2 de PHP et les fonctions ne dépendent plus de la locale. + +C'est un bel exemple de la manière dont l'état global a tourmenté des milliers de développeurs dans le monde. La solution a été de le remplacer par l'injection de dépendances. + + +Quand est-il possible d'utiliser l'état global ? .[#toc-when-is-it-possible-to-use-global-state] +------------------------------------------------------------------------------------------------ + +Dans certaines situations spécifiques, il est possible d'utiliser l'état global. Par exemple, lorsque vous déboguez un code et que vous avez besoin d'extraire la valeur d'une variable ou de mesurer la durée d'une partie spécifique du programme. Dans de tels cas, qui concernent des actions temporaires qui seront ultérieurement supprimées du code, il est légitime d'utiliser un dumper ou un chronomètre disponible globalement. Ces outils ne font pas partie de la conception du code. + +Un autre exemple est celui des fonctions permettant de travailler avec des expressions régulières `preg_*`, qui stockent en interne les expressions régulières compilées dans un cache statique en mémoire. Lorsque vous appelez la même expression régulière plusieurs fois dans différentes parties du code, elle n'est compilée qu'une seule fois. Le cache permet de gagner en performance et est totalement invisible pour l'utilisateur, de sorte qu'une telle utilisation peut être considérée comme légitime. + + +Résumé .[#toc-summary] +---------------------- + +Nous avons montré pourquoi il est logique + +1) Supprimer toutes les variables statiques du code +2) Déclarer les dépendances +3) Et utiliser l'injection de dépendances + +Lorsque vous envisagez la conception d'un code, gardez à l'esprit que chaque `static $foo` représente un problème. Pour que votre code soit un environnement respectueux de l'injection de dépendances, il est essentiel d'éradiquer complètement l'état global et de le remplacer par l'injection de dépendances. + +Au cours de ce processus, vous constaterez peut-être que vous devez diviser une classe parce qu'elle a plus d'une responsabilité. Ne vous en préoccupez pas ; efforcez-vous de respecter le principe de la responsabilité unique. + +*Je tiens à remercier Miško Hevery, dont les articles tels que [Flaw : Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] constituent la base de ce chapitre*. diff --git a/dependency-injection/hu/@home.texy b/dependency-injection/hu/@home.texy index 6bb246f739..5eae0f35f9 100644 --- a/dependency-injection/hu/@home.texy +++ b/dependency-injection/hu/@home.texy @@ -5,8 +5,9 @@ Függőségi injekció A Dependency Injection egy olyan tervezési minta, amely alapvetően megváltoztatja a kód és a fejlesztés szemléletét. Megnyitja az utat a tisztán megtervezett és fenntartható alkalmazások világa felé. - [Mi az a Dependency Injection? |introduction] -- [Mi az a DI Container? |container] +- [Globális állapot és singletonok |global-state] - [Függőségek átadása |passing-dependencies] +- [Mi az a DI Container? |container] Nette DI diff --git a/dependency-injection/hu/@left-menu.texy b/dependency-injection/hu/@left-menu.texy index b1dcdbae58..c17a237f18 100644 --- a/dependency-injection/hu/@left-menu.texy +++ b/dependency-injection/hu/@left-menu.texy @@ -1,8 +1,9 @@ Függőségi injekció ****************** - [Mi az a DI? |introduction] -- [Mi az a DI Container? |container] +- [Globális állapot és singletonok |global-state] - [Függőségek átadása |passing-dependencies] +- [Mi az a DI Container? |container] Nette DI diff --git a/dependency-injection/hu/global-state.texy b/dependency-injection/hu/global-state.texy new file mode 100644 index 0000000000..efcae52950 --- /dev/null +++ b/dependency-injection/hu/global-state.texy @@ -0,0 +1,312 @@ +Globális állapot és singletonok +******************************* + +.[perex] +Figyelmeztetés: A következő konstrukciók a rossz kódtervezés tünetei: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` vagy `static::$var` + +Előfordulnak-e ezek a konstrukciók az Ön kódjában? Akkor lehetősége van a javításra. Talán arra gondolsz, hogy ezek olyan gyakori konstrukciók, amelyeket különböző könyvtárak és keretrendszerek mintamegoldásaiban látunk. +Sajnos ezek még mindig a rossz tervezés egyértelmű jelei. Egy dolog közös bennük: a globális állapot használata. + +Most biztosan nem valamiféle akadémiai tisztaságról beszélünk. A globális állapot és a singletonok használata romboló hatással van a kód minőségére. A viselkedés kiszámíthatatlanná válik, csökkenti a fejlesztők termelékenységét, és arra kényszeríti az osztályinterfészeket, hogy hazudjanak a valódi függőségükről. Ami összezavarja a programozókat. + +Ebben a fejezetben megmutatjuk, hogyan lehetséges ez. + + +Globális összekapcsolás .[#toc-global-interlinking] +--------------------------------------------------- + +A globális állammal az az alapvető probléma, hogy globálisan hozzáférhető. Ez lehetővé teszi, hogy a `DB::insert()` globális (statikus) metóduson keresztül írjunk az adatbázisba. +Egy ideális világban egy objektumnak csak olyan más objektumokkal kellene tudnia kommunikálni, amelyeket [közvetlenül átadtak neki |passing-dependencies]. +Ha létrehozok két objektumot `A` és `B`, és soha nem adok át hivatkozást a `A` objektumról a `B` objektumra, akkor sem a `A`, sem a `B` nem tud hozzáférni a másik objektumhoz, és nem tudja megváltoztatni annak állapotát. +Ez egy nagyon kívánatos tulajdonsága a kódnak. Hasonló ahhoz, mintha lenne egy elem és egy izzó; az izzó nem fog világítani, amíg össze nem kapcsoljuk őket. + +Ez nem igaz a globális (statikus) változókra vagy a szingletonokra. A `A` objektum *vezeték nélkül* hozzáférhet a `C` objektumhoz, és módosíthatja azt hivatkozás átadása nélkül, a `C::changeSomething()` meghívásával. +Ha a `B` objektum a globális `C` objektumot is megragadja, akkor a `A` és a `B` objektum a `C` objektumon keresztül kölcsönhatásba léphet egymással. + +A globális változók használata a *vezeték nélküli* csatolás egy új, kívülről nem látható formáját vezeti be a rendszerbe. +Ez egy füstfüggönyt hoz létre, amely megnehezíti a kód megértését és használatát. +A fejlesztőknek a forráskód minden sorát el kell olvasniuk ahhoz, hogy valóban megértsék a függőségeket. Ahelyett, hogy csak az osztályok interfészével ismerkednének. +Ráadásul ez egy teljesen felesleges csatolás. + +.[note] +A viselkedés szempontjából nincs különbség egy globális és egy statikus változó között. Egyformán károsak. + + +A kísérteties cselekvés távolról .[#toc-the-spooky-action-at-a-distance] +------------------------------------------------------------------------ + +"Kísérteties hatás a távolban" - így nevezte Albert Einstein 1935-ben a kvantumfizika egyik jelenségét, amelytől kirázta a hideg. +Ez a kvantum összefonódás, amelynek sajátossága, hogy amikor egy részecskéről információt mérünk, azonnal hatással vagyunk egy másik részecskére, még akkor is, ha azok több millió fényévre vannak egymástól. +ami látszólag sérti a világegyetem alapvető törvényét, miszerint semmi sem haladhat gyorsabban a fénynél. + +A szoftverek világában "spooky action at a distance"-nek nevezhetjük azt a helyzetet, amikor lefuttatunk egy folyamatot, amelyről azt gondoljuk, hogy elszigetelt (mert nem adtunk át neki semmilyen hivatkozást), de váratlan kölcsönhatások és állapotváltozások történnek a rendszer távoli pontjain, amelyekről nem szóltunk az objektumnak. Ez csak a globális állapoton keresztül történhet. + +Képzeljük el, hogy csatlakozunk egy olyan projektfejlesztő csapathoz, amely nagy, kiforrott kódbázissal rendelkezik. Az új vezetőd megkér egy új funkció megvalósítására, és jó fejlesztőhöz méltóan egy teszt megírásával kezded. De mivel új vagy a projektben, sok feltáró "mi történik, ha meghívom ezt a metódust" típusú tesztet csinálsz. És megpróbálod megírni a következő tesztet: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // az Ön kártyaszámát + $cc->charge(100); +} +``` + +Egy idő után észreveszed, hogy a bank értesítést küld a telefonodon, hogy minden egyes futtatáskor 100 dollárral terhelték meg a hitelkártyádat. 🤦‍♂️ + +Hogy a fenébe okozhatott a teszt tényleges terhelést? Nem könnyű a hitelkártyával operálni. Egy harmadik fél webes szolgáltatásával kell kapcsolatba lépnie, ismernie kell a webes szolgáltatás URL-jét, be kell jelentkeznie, és így tovább. +Ezek közül az információk közül egyik sem szerepel a tesztben. Még rosszabb, hogy azt sem tudod, hol vannak ezek az információk, és ezért nem tudod, hogyan kell a külső függőségeket leutánozni, hogy minden egyes futtatásnál ne kelljen újra 100 dollárt fizetni. És új fejlesztőként honnan kellett volna tudnod, hogy amit most fogsz csinálni, az 100 dollárral szegényebbé tesz téged? + +Ez egy kísérteties akció a távolból! + +Nincs más választásod, mint rengeteg forráskódban turkálni, megkérdezni idősebb és tapasztaltabb kollégákat, amíg meg nem érted, hogyan működnek az összefüggések a projektben. +Ennek oka, hogy a `CreditCard` osztály interfészét megnézve nem tudod meghatározni az inicializálandó globális állapotot. Még az osztály forráskódjának megnézése sem árulja el, hogy melyik inicializálási metódust kell meghívni. A legjobb esetben megkereshetjük a globális változót, amelyhez hozzáférünk, és ebből próbálhatjuk kitalálni, hogyan kell inicializálni. + +Egy ilyen projektben az osztályok beteges hazudozók. A fizetési kártya úgy tesz, mintha egyszerűen csak instanciáznád, és meghívnád a `charge()` metódust. Titokban azonban kölcsönhatásba lép egy másik osztállyal, a `PaymentGateway`. Még az interfésze is azt mondja, hogy önállóan inicializálható, de a valóságban valamilyen konfigurációs fájlból húzza a hitelesítő adatokat, és így tovább. +A kódot író fejlesztők számára egyértelmű, hogy a `CreditCard` -nak szüksége van a `PaymentGateway`. Így írták meg a kódot. De bárki számára, aki új a projektben, ez teljes rejtély, és akadályozza a tanulást. + +Hogyan lehet kijavítani a helyzetet? Egyszerűen. **Hagyjuk, hogy az API deklarálja a függőségeket.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Figyeljük meg, hogy a kódon belüli kapcsolatok hirtelen nyilvánvalóvá válnak. Azzal, hogy deklaráljuk, hogy a `charge()` metódusnak szüksége van a `PaymentGateway` címre, senkitől sem kell megkérdeznünk, hogy a kód hogyan függ egymástól. Tudod, hogy egy példányt kell létrehoznod belőle, és amikor megpróbálod ezt megtenni, belefutsz abba, hogy hozzáférési paramétereket kell megadnod. Ezek nélkül a kód nem is futna. + +És ami a legfontosabb, most már le tudja mockolni a fizetési átjárót, hogy ne kelljen 100 dollárt fizetnie minden egyes teszt futtatásakor. + +A globális állapot miatt az objektumaid titokban hozzáférhetnek olyan dolgokhoz, amelyek nincsenek deklarálva az API-jukban, és ennek eredményeképpen az API-id kóros hazudozókká válnak. + +Lehet, hogy eddig nem gondoltál rá így, de valahányszor globális állapotot használsz, titkos vezeték nélküli kommunikációs csatornákat hozol létre. A hátborzongató távoli működés arra kényszeríti a fejlesztőket, hogy minden egyes kódsort elolvassanak a lehetséges interakciók megértéséhez, csökkenti a fejlesztők termelékenységét, és összezavarja az új csapattagokat. +Ha te vagy az, aki a kódot létrehozta, akkor ismered a valódi függőségeket, de bárki, aki utánad jön, tanácstalan. + +Ne írjon olyan kódot, amely globális állapotot használ, inkább adja át a függőségeket. Vagyis a függőségi injektálás. + + +A globális állam törékenysége .[#toc-brittleness-of-the-global-state] +--------------------------------------------------------------------- + +A globális állapotot és singletonokat használó kódban sosem lehetünk biztosak abban, hogy az állapotot mikor és ki változtatta meg. Ez a kockázat már az inicializáláskor fennáll. A következő kódnak egy adatbázis-kapcsolatot kellene létrehoznia és inicializálnia a fizetési átjárót, de folyamatosan kivételt dob, és az okának megtalálása rendkívül fárasztó: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Részletesen át kell nézni a kódot, hogy kiderüljön, hogy a `PaymentGateway` objektum vezeték nélkül más objektumokhoz is hozzáfér, amelyek közül néhányhoz adatbázis-kapcsolat szükséges. Így a `PaymentGateway` előtt inicializálni kell az adatbázist. A globális állapot füstfüggönye azonban ezt elrejti Ön elől. Mennyi időt spórolna meg, ha az egyes osztályok API-ja nem hazudna és nem jelentené be függőségeit? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Hasonló probléma merül fel, amikor globális hozzáférést használunk egy adatbázis-kapcsolathoz: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +A `save()` metódus meghívásakor nem biztos, hogy az adatbázis-kapcsolat már létrejött-e, és ki a felelős a létrehozásáért. Ha például menet közben szeretnénk megváltoztatni az adatbázis-kapcsolatot, esetleg tesztelési céllal, akkor valószínűleg további metódusokat kellene létrehoznunk, például a `DB::reconnect(...)` vagy a `DB::reconnectForTest()` metódusokat. + +Vegyünk egy példát: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Hol lehetünk biztosak abban, hogy a `$article->save()` meghívásakor valóban a tesztadatbázist használjuk ? Mi van, ha a `Foo::doSomething()` módszer megváltoztatta a globális adatbázis-kapcsolatot? Ahhoz, hogy ezt megtudjuk, meg kellene vizsgálnunk a `Foo` osztály és valószínűleg sok más osztály forráskódját. Ez a megközelítés azonban csak rövid távú választ adna, mivel a jövőben változhat a helyzet. + +Mi lenne, ha az adatbázis-kapcsolatot egy statikus változóba helyeznénk át a `Article` osztályon belül? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Ez egyáltalán nem változtat semmit. A probléma egy globális állapot, és nem számít, hogy melyik osztályban rejtőzik. Ebben az esetben, ahogy az előző esetben is, fogalmunk sincs arról, hogy a `$article->save()` metódus meghívásakor milyen adatbázisba íródik. Bárki az alkalmazás távoli végén bármikor megváltoztathatja az adatbázist a `Article::setDb()` segítségével. A mi kezünk alatt. + +A globális állapot miatt az alkalmazásunk **rendkívül törékennyé** válik. + +Van azonban egy egyszerű módja ennek a problémának a kezelésére. Csak az API-nak kell deklarálnia a függőségeket a megfelelő funkcionalitás biztosítása érdekében. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Ez a megközelítés kiküszöböli az adatbázis-kapcsolatok rejtett és váratlan változásai miatti aggodalmat. Most már biztosak vagyunk abban, hogy a cikket hol tároljuk, és semmilyen kódmódosítás egy másik, nem kapcsolódó osztályon belül nem változtathatja meg többé a helyzetet. A kód többé nem törékeny, hanem stabil. + +Ne írjunk olyan kódot, amely globális állapotot használ, inkább adjuk át a függőségeket. Így a függőségi injektálás. + + +Singleton .[#toc-singleton] +--------------------------- + +A singleton egy olyan tervezési minta, amely a híres Gang of Four kiadvány [definíciója |https://en.wikipedia.org/wiki/Singleton_pattern] szerint egy osztályt egyetlen példányra korlátoz, és globális hozzáférést biztosít hozzá. Ennek a mintának a megvalósítása általában a következő kódhoz hasonlít: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // és más metódusok, amelyek az osztály funkcióit hajtják végre. +} +``` + +Sajnos a singleton globális állapotot vezet be az alkalmazásba. És mint fentebb megmutattuk, a globális állapot nem kívánatos. Ezért tekinthető a singleton antipatternnek. + +Ne használjon singletont a kódjában, és helyettesítse más mechanizmusokkal. Tényleg nincs szükséged szingletonokra. Ha azonban garantálnod kell egy osztály egyetlen példányának létezését az egész alkalmazás számára, akkor hagyd ezt a [DI konténerre |container]. +Így hozzon létre egy alkalmazás szingletont, vagy szolgáltatást. Ezáltal az osztály nem fogja biztosítani a saját egyediségét (azaz nem lesz `getInstance()` metódusa és statikus változója), és csak a funkcióit fogja végrehajtani. Így megszűnik az egyetlen felelősség elvének megsértése. + + +Globális állapot a tesztek ellenében .[#toc-global-state-versus-tests] +---------------------------------------------------------------------- + +A tesztek írása során feltételezzük, hogy minden teszt egy izolált egység, és nem kerül bele külső állapot. És semmilyen állapot nem hagyja el a teszteket. Amikor egy teszt befejeződik, a teszthez kapcsolódó állapotot a szemétgyűjtőnek automatikusan el kell távolítania. Ez teszi a teszteket izolálttá. Ezért a teszteket tetszőleges sorrendben futtathatjuk. + +Ha azonban globális állapotok/singletonok vannak jelen, akkor mindezek a szép feltételezések összeomlanak. Egy állapot beléphet és kiléphet egy tesztből. Hirtelen a tesztek sorrendje számíthat. + +Ahhoz, hogy a szingletonokat egyáltalán tesztelni lehessen, a fejlesztőknek gyakran lazítaniuk kell a tulajdonságaikon, például úgy, hogy megengedik, hogy egy példányt egy másikra cseréljenek. Az ilyen megoldások a legjobb esetben is hackek, amelyek nehezen karbantartható és nehezen érthető kódot eredményeznek. Minden olyan tesztnek vagy metódusnak `tearDown()`, amely bármilyen globális állapotot érint, vissza kell vonnia ezeket a változásokat. + +A globális állapot a legnagyobb fejfájás az egységtesztelésben! + +Hogyan lehet megoldani a helyzetet? Egyszerűen. Ne írj olyan kódot, amely singletonokat használ, inkább add át a függőségeket. Vagyis függőségi injektálással. + + +Globális konstansok .[#toc-global-constants] +-------------------------------------------- + +A globális állapot nem korlátozódik a szingletonok és statikus változók használatára, hanem a globális konstansokra is vonatkozhat. + +Azok a konstansok, amelyek értéke nem szolgáltat számunkra új (`M_PI`) vagy hasznos (`PREG_BACKTRACK_LIMIT_ERROR`) információt, egyértelműen rendben vannak. +Ezzel szemben azok a konstansok, amelyek arra szolgálnak, hogy *vezeték nélkül* információt adjunk át a kódon belül, nem többek, mint rejtett függőség. Mint a `LOG_FILE` a következő példában. +A `FILE_APPEND` konstans használata teljesen helyes. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Ebben az esetben a paramétert a `Foo` osztály konstruktorában kell deklarálnunk, hogy az API részévé váljon: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Most már átadhatjuk a naplófájl elérési útvonalára vonatkozó információt, és szükség esetén könnyen módosíthatjuk azt, így könnyebben tesztelhetjük és karbantarthatjuk a kódot. + + +Globális függvények és statikus metódusok .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------------------------- + +Szeretnénk hangsúlyozni, hogy a statikus metódusok és globális függvények használata önmagában nem problémás. A `DB::insert()` és hasonló módszerek használatának helytelenségét már elmagyaráztuk, de mindig is a statikus változóban tárolt globális állapotról volt szó. A `DB::insert()` metódus megköveteli egy statikus változó meglétét, mivel az adatbázis-kapcsolatot tárolja. E változó nélkül lehetetlen lenne a módszer végrehajtása. + +A determinisztikus statikus módszerek és függvények, mint például a `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` és sok más, használata tökéletesen összhangban van a függőségi injektálással. Ezek a függvények mindig ugyanazokat az eredményeket adják vissza ugyanazokból a bemeneti paraméterekből, ezért kiszámíthatóak. Nem használnak semmilyen globális állapotot. + +Vannak azonban a PHP-ben olyan függvények, amelyek nem determinisztikusak. Ezek közé tartozik például a `htmlspecialchars()` függvény. Harmadik paramétere, a `$encoding`, ha nincs megadva, alapértelmezés szerint a `ini_get('default_charset')` konfigurációs opció értékét veszi fel. Ezért ajánlott mindig megadni ezt a paramétert, hogy elkerüljük a függvény esetleges kiszámíthatatlan viselkedését. A Nette következetesen ezt teszi. + +Egyes függvények, mint például a `strtolower()`, `strtoupper()` és hasonlók, a közelmúltban nem determinisztikus viselkedést mutattak, és a `setlocale()` beállításától függtek. Ez sok bonyodalmat okozott, leggyakrabban a török nyelvvel való munka során. +Ennek oka, hogy a török nyelv különbséget tesz a kis- és nagybetűs `I` között ponttal és pont nélkül. Így a `strtolower('I')` a `ı` karaktert, a `strtoupper('i')` pedig a `İ` karaktert adta vissza , ami az alkalmazásokban számos rejtélyes hibát okozott. +Ezt a problémát azonban a PHP 8.2-es verziójában kijavították, és a függvények többé nem függnek a nyelvjárástól. + +Ez egy szép példa arra, hogy a globális állapot fejlesztők ezreit sújtotta világszerte. A megoldás az volt, hogy függőségi injektálással helyettesítették. + + +Mikor lehetséges a globális állapot használata? .[#toc-when-is-it-possible-to-use-global-state] +----------------------------------------------------------------------------------------------- + +Vannak bizonyos speciális helyzetek, amikor lehetséges a globális állapot használata. Például kód hibakereséskor, amikor ki kell dobni egy változó értékét, vagy meg kell mérni a program egy adott részének időtartamát. Ilyen esetekben, amelyek olyan ideiglenes műveletekre vonatkoznak, amelyeket később eltávolítunk a kódból, jogos egy globálisan elérhető dumper vagy stopperóra használata. Ezek az eszközök nem részei a kódtervezésnek. + +Egy másik példa a `preg_*`, a reguláris kifejezésekkel való munkavégzésre szolgáló függvények, amelyek a lefordított reguláris kifejezéseket belsőleg egy statikus gyorsítótárban tárolják a memóriában. Ha ugyanazt a reguláris kifejezést a kód különböző részeiben többször hívja meg, akkor csak egyszer fordítja le. A gyorsítótár teljesítményt takarít meg, és a felhasználó számára is teljesen láthatatlan, így az ilyen használat jogosnak tekinthető. + + +Összefoglaló .[#toc-summary] +---------------------------- + +Megmutattuk, miért van értelme + +1) Távolítsunk el minden statikus változót a kódból. +2) Deklaráljuk a függőségeket +3) És használjuk a függőségi injektálást + +Amikor a kódtervezésről gondolkodik, tartsa szem előtt, hogy minden egyes `static $foo` egy problémát jelent. Ahhoz, hogy a kódod DI-tisztelő környezet legyen, elengedhetetlen a globális állapot teljes kiirtása és függőségi injektálással való helyettesítése. + +E folyamat során előfordulhat, hogy egy osztályt fel kell osztanod, mert egynél több felelőssége van. Ne aggódjon emiatt; törekedjen az egy felelősség elvére. + +*Köszönöm Miško Hevery-nek, akinek olyan cikkei, mint a [Flaw: Brittle Global State & Singletons (Hiba: Törékeny globális állapot és szingletonok |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] ) képezik e fejezet alapját.* diff --git a/dependency-injection/it/@home.texy b/dependency-injection/it/@home.texy index 8a6e1181bb..bb96a23174 100644 --- a/dependency-injection/it/@home.texy +++ b/dependency-injection/it/@home.texy @@ -5,8 +5,9 @@ Iniezione di dipendenza La Dependency Injection è un modello di progettazione che cambierà radicalmente il modo di vedere il codice e lo sviluppo. Apre la strada a un mondo di applicazioni pulite e sostenibili. - [Che cos'è la Dependency Injection? |introduction] +- [Stato globale e singleton |global-state] +- [Passare le dipendenze |passing-dependencies] - [Cos'è il contenitore DI? |container] -- [Passaggio delle dipendenze |passing-dependencies] Nette DI diff --git a/dependency-injection/it/@left-menu.texy b/dependency-injection/it/@left-menu.texy index fd8cbdee19..c52d00d7af 100644 --- a/dependency-injection/it/@left-menu.texy +++ b/dependency-injection/it/@left-menu.texy @@ -1,8 +1,9 @@ Iniezione di dipendenza *********************** - [Che cos'è DI? |introduction] +- [Stato globale e singleton |global-state] +- [Passare le dipendenze |passing-dependencies] - [Che cos'è il contenitore DI? |container] -- [Passaggio di dipendenze |passing-dependencies] Nette DI diff --git a/dependency-injection/it/global-state.texy b/dependency-injection/it/global-state.texy new file mode 100644 index 0000000000..f48ba87e7f --- /dev/null +++ b/dependency-injection/it/global-state.texy @@ -0,0 +1,312 @@ +Stato globale e singoletti +************************** + +.[perex] +Attenzione: i seguenti costrutti sono sintomi di una cattiva progettazione del codice: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` o `static::$var` + +Qualcuno di questi costrutti è presente nel vostro codice? Allora avete l'opportunità di migliorare. Potreste pensare che si tratta di costrutti comuni che vediamo nelle soluzioni di esempio di varie librerie e framework. +Sfortunatamente, sono comunque un chiaro indicatore di cattiva progettazione. Hanno una cosa in comune: l'uso dello stato globale. + +Ora, non stiamo certo parlando di una sorta di purezza accademica. L'uso dello stato globale e dei singleton ha effetti distruttivi sulla qualità del codice. Il suo comportamento diventa imprevedibile, riduce la produttività degli sviluppatori e costringe le interfacce delle classi a mentire sulle loro vere dipendenze. Il che confonde i programmatori. + +In questo capitolo mostreremo come ciò sia possibile. + + +Interconnessione globale .[#toc-global-interlinking] +---------------------------------------------------- + +Il problema fondamentale dello stato globale è che è accessibile a livello globale. Questo rende possibile scrivere sul database tramite il metodo globale (statico) `DB::insert()`. +In un mondo ideale, un oggetto dovrebbe essere in grado di comunicare solo con altri oggetti che gli sono stati [passati direttamente |passing-dependencies]. +Se creo due oggetti `A` e `B` e non passo mai un riferimento da `A` a `B`, né `A`, né `B` possono accedere all'altro oggetto o modificarne lo stato. +Questa è una caratteristica molto desiderabile del codice. È come avere una batteria e una lampadina; la lampadina non si accende finché non si collegano i due oggetti. + +Questo non vale per le variabili globali (statiche) o per i singleton. L'oggetto `A` potrebbe accedere *senza fili* all'oggetto `C` e modificarlo senza passare alcun riferimento, chiamando `C::changeSomething()`. +Se l'oggetto `B` prende anche la variabile globale `C`, allora `A` e `B` possono interagire tra loro tramite `C`. + +L'uso di variabili globali introduce una nuova forma di accoppiamento *wireless* nel sistema, non visibile dall'esterno. +Crea una cortina di fumo che complica la comprensione e l'uso del codice. +Gli sviluppatori devono leggere ogni riga del codice sorgente per capire veramente le dipendenze. Invece di limitarsi a familiarizzare con l'interfaccia delle classi. +Inoltre, si tratta di un accoppiamento del tutto inutile. + +.[note] +In termini di comportamento, non c'è differenza tra una variabile globale e una statica. Sono ugualmente dannose. + + +L'azione spettrale a distanza .[#toc-the-spooky-action-at-a-distance] +--------------------------------------------------------------------- + +"Azione spettrale a distanza": così Albert Einstein definì nel 1935 un fenomeno della fisica quantistica che gli fece venire i brividi. +Si tratta dell'entanglement quantistico, la cui peculiarità è che quando si misura un'informazione su una particella, si influisce immediatamente su un'altra particella, anche se si trovano a milioni di anni luce di distanza. +Il che sembra violare la legge fondamentale dell'universo secondo cui nulla può viaggiare più veloce della luce. + +Nel mondo del software, possiamo chiamare "azione spettrale a distanza" una situazione in cui eseguiamo un processo che pensiamo sia isolato (perché non gli abbiamo passato alcun riferimento), ma interazioni inaspettate e cambiamenti di stato avvengono in posizioni distanti del sistema di cui non abbiamo parlato all'oggetto. Questo può avvenire solo attraverso lo stato globale. + +Immaginate di entrare in un team di sviluppo di un progetto che ha una base di codice ampia e matura. Il vostro nuovo capo vi chiede di implementare una nuova funzionalità e, da bravi sviluppatori, iniziate scrivendo un test. Ma poiché siete nuovi nel progetto, fate molti test esplorativi del tipo "cosa succede se chiamo questo metodo". E provate a scrivere il seguente test: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // il numero della carta + $cc->charge(100); +} +``` + +Eseguite il codice, magari più volte, e dopo un po' notate sul vostro telefono le notifiche della banca che ogni volta che lo eseguite, 100 dollari sono stati addebitati sulla vostra carta di credito 🤦‍♂️ + +Come può il test causare un addebito effettivo? Non è facile operare con la carta di credito. Bisogna interagire con un servizio web di terze parti, conoscere l'URL di tale servizio web, effettuare il login e così via. +Nessuna di queste informazioni è inclusa nel test. Ancora peggio, non si sa nemmeno dove siano presenti queste informazioni e quindi come prendere in giro le dipendenze esterne in modo che ogni esecuzione non comporti un nuovo addebito di 100 dollari. E come nuovo sviluppatore, come potevate sapere che quello che stavate per fare vi avrebbe fatto perdere 100 dollari? + +È un'azione spettrale a distanza! + +Non avete altra scelta se non quella di scavare nel codice sorgente, chiedendo ai colleghi più anziani e più esperti, fino a capire come funzionano le connessioni nel progetto. +Ciò è dovuto al fatto che, guardando l'interfaccia della classe `CreditCard`, non è possibile determinare lo stato globale che deve essere inizializzato. Anche guardando il codice sorgente della classe non si può sapere quale metodo di inizializzazione chiamare. Al massimo, si può trovare la variabile globale a cui si accede e cercare di indovinare come inizializzarla a partire da essa. + +Le classi in un progetto di questo tipo sono bugiarde patologiche. La carta di pagamento finge di poter essere semplicemente istanziata e di poter chiamare il metodo `charge()`. Tuttavia, interagisce segretamente con un'altra classe, `PaymentGateway`. Anche la sua interfaccia dice che può essere inizializzata in modo indipendente, ma in realtà preleva le credenziali da qualche file di configurazione e così via. +Per gli sviluppatori che hanno scritto questo codice è chiaro che `CreditCard` ha bisogno di `PaymentGateway`. Hanno scritto il codice in questo modo. Ma per chiunque sia nuovo al progetto, questo è un completo mistero e ostacola l'apprendimento. + +Come risolvere la situazione? Semplice. **Lasciare che l'API dichiari le dipendenze.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Si noti come le relazioni all'interno del codice siano improvvisamente evidenti. Dichiarando che il metodo `charge()` ha bisogno di `PaymentGateway`, non è necessario chiedere a nessuno come il codice sia interdipendente. Si sa che bisogna crearne un'istanza e, quando si cerca di farlo, ci si imbatte nel fatto che bisogna fornire dei parametri di accesso. Senza di essi, il codice non potrebbe nemmeno funzionare. + +E soprattutto, ora potete prendere in giro il gateway di pagamento, così non vi verranno addebitati 100 dollari ogni volta che eseguite un test. + +Lo stato globale fa sì che i vostri oggetti possano accedere segretamente a cose che non sono dichiarate nelle loro API e, di conseguenza, rende le vostre API dei bugiardi patologici. + +Forse non ci avete mai pensato prima, ma ogni volta che usate lo stato globale, state creando canali di comunicazione wireless segreti. Le inquietanti azioni remote costringono gli sviluppatori a leggere ogni riga di codice per capire le potenziali interazioni, riducono la produttività degli sviluppatori e confondono i nuovi membri del team. +Se siete voi a creare il codice, conoscete le vere dipendenze, ma chi viene dopo di voi non sa nulla. + +Non scrivete codice che utilizza lo stato globale, ma preferite passare le dipendenze. Ovvero, la dependency injection. + + +La fragilità dello Stato globale .[#toc-brittleness-of-the-global-state] +------------------------------------------------------------------------ + +Nel codice che utilizza lo stato globale e i singleton, non è mai certo quando e da chi lo stato è stato modificato. Questo rischio è già presente al momento dell'inizializzazione. Il codice seguente dovrebbe creare una connessione al database e inizializzare il gateway di pagamento, ma continua a lanciare un'eccezione e trovare la causa è estremamente noioso: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +È necessario esaminare il codice in dettaglio per scoprire che l'oggetto `PaymentGateway` accede ad altri oggetti in modalità wireless, alcuni dei quali richiedono una connessione al database. Pertanto, è necessario inizializzare il database prima di `PaymentGateway`. Tuttavia, la cortina di fumo dello stato globale nasconde questo aspetto. Quanto tempo si risparmierebbe se l'API di ogni classe non mentisse e non dichiarasse le sue dipendenze? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Un problema simile si presenta quando si utilizza l'accesso globale a una connessione di database: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Quando si chiama il metodo `save()`, non si sa se è già stata creata una connessione al database e chi è responsabile della sua creazione. Ad esempio, se si volesse cambiare al volo la connessione al database, magari a scopo di test, probabilmente si dovrebbero creare metodi aggiuntivi come `DB::reconnect(...)` o `DB::reconnectForTest()`. + +Consideriamo un esempio: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Come possiamo essere sicuri che il database di prova sia davvero utilizzato quando chiamiamo `$article->save()`? E se il metodo `Foo::doSomething()` cambiasse la connessione globale al database? Per scoprirlo, dovremmo esaminare il codice sorgente della classe `Foo` e probabilmente di molte altre classi. Tuttavia, questo approccio fornirebbe solo una risposta a breve termine, poiché la situazione potrebbe cambiare in futuro. + +E se spostassimo la connessione al database in una variabile statica all'interno della classe `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Questo non cambia assolutamente nulla. Il problema è uno stato globale e non importa in quale classe si nasconda. In questo caso, come in quello precedente, non abbiamo alcun indizio su quale database viene scritto quando viene chiamato il metodo `$article->save()`. Chiunque, all'estremità distante dell'applicazione, potrebbe cambiare il database in qualsiasi momento usando `Article::setDb()`. Sotto le nostre mani. + +Lo stato globale rende la nostra applicazione **estremamente fragile**. + +Tuttavia, esiste un modo semplice per affrontare questo problema. Basta che l'API dichiari le dipendenze per garantire la corretta funzionalità. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Questo approccio elimina la preoccupazione di modifiche nascoste e inaspettate alle connessioni al database. Ora siamo sicuri di dove è memorizzato l'articolo e nessuna modifica del codice all'interno di un'altra classe non correlata può più cambiare la situazione. Il codice non è più fragile, ma stabile. + +Non scrivete codice che utilizza lo stato globale, ma preferite passare le dipendenze. Quindi, l'iniezione di dipendenza. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton è un pattern di progettazione che, secondo [la definizione |https://en.wikipedia.org/wiki/Singleton_pattern] della famosa pubblicazione Gang of Four, limita una classe a una singola istanza e offre un accesso globale a essa. L'implementazione di questo pattern di solito assomiglia al codice seguente: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // e altri metodi che svolgono le funzioni della classe +} +``` + +Purtroppo, il singleton introduce lo stato globale nell'applicazione. E come abbiamo mostrato in precedenza, lo stato globale è indesiderabile. Ecco perché il singleton è considerato un antipattern. + +Non utilizzate i singleton nel vostro codice e sostituiteli con altri meccanismi. I singleton non sono necessari. Tuttavia, se è necessario garantire l'esistenza di una singola istanza di una classe per l'intera applicazione, bisogna lasciarla al [contenitore DI |container]. +Quindi, creare un singleton dell'applicazione, o un servizio. Questo impedirà alla classe di fornire la propria unicità (cioè, non avrà un metodo `getInstance()` e una variabile statica) ed eseguirà solo le sue funzioni. In questo modo, non violerà più il principio della responsabilità unica. + + +Stato globale e test .[#toc-global-state-versus-tests] +------------------------------------------------------ + +Quando si scrivono i test, si assume che ogni test sia un'unità isolata e che nessuno stato esterno vi entri. E nessuno stato lascia i test. Quando un test viene completato, qualsiasi stato associato al test dovrebbe essere rimosso automaticamente dal garbage collector. Questo rende i test isolati. Pertanto, possiamo eseguire i test in qualsiasi ordine. + +Tuttavia, se sono presenti stati/singleton globali, tutte queste belle assunzioni vengono meno. Uno stato può entrare e uscire da un test. Improvvisamente, l'ordine dei test può essere importante. + +Per testare i singleton, gli sviluppatori devono spesso rilassare le loro proprietà, magari permettendo a un'istanza di essere sostituita da un'altra. Queste soluzioni sono, nella migliore delle ipotesi, dei trucchi che producono codice difficile da mantenere e da capire. Qualsiasi test o metodo `tearDown()` che influisca su uno stato globale deve annullare tali modifiche. + +Lo stato globale è il più grande grattacapo dei test unitari! + +Come risolvere la situazione? Semplice. Non scrivete codice che utilizza singleton, ma preferite passare le dipendenze. Ovvero, la dependency injection. + + +Costanti globali .[#toc-global-constants] +----------------------------------------- + +Lo stato globale non è limitato all'uso di singleton e variabili statiche, ma può essere applicato anche alle costanti globali. + +Le costanti il cui valore non ci fornisce alcuna informazione nuova (`M_PI`) o utile (`PREG_BACKTRACK_LIMIT_ERROR`) sono chiaramente OK. +Al contrario, le costanti che servono a passare *senza fili* informazioni all'interno del codice non sono altro che una dipendenza nascosta. Come `LOG_FILE` nell'esempio seguente. +L'uso della costante `FILE_APPEND` è perfettamente corretto. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +In questo caso, dovremmo dichiarare il parametro nel costruttore della classe `Foo` per renderlo parte dell'API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Ora possiamo passare le informazioni sul percorso del file di registrazione e modificarle facilmente in base alle necessità, rendendo più facili i test e la manutenzione del codice. + + +Funzioni globali e metodi statici .[#toc-global-functions-and-static-methods] +----------------------------------------------------------------------------- + +Vogliamo sottolineare che l'uso di metodi statici e funzioni globali non è di per sé problematico. Abbiamo spiegato l'inadeguatezza dell'uso di `DB::insert()` e di metodi simili, ma si è sempre trattato di uno stato globale memorizzato in una variabile statica. Il metodo `DB::insert()` richiede l'esistenza di una variabile statica perché memorizza la connessione al database. Senza questa variabile, sarebbe impossibile implementare il metodo. + +L'uso di metodi e funzioni statiche deterministiche, come `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` e molti altri, è perfettamente coerente con la dependency injection. Queste funzioni restituiscono sempre gli stessi risultati dagli stessi parametri di ingresso e sono quindi prevedibili. Non utilizzano alcuno stato globale. + +Tuttavia, esistono funzioni in PHP che non sono deterministiche. Tra queste c'è, per esempio, la funzione `htmlspecialchars()`. Il suo terzo parametro, `$encoding`, se non specificato, assume per default il valore dell'opzione di configurazione `ini_get('default_charset')`. Pertanto, si raccomanda di specificare sempre questo parametro per evitare un comportamento imprevedibile della funzione. Nette fa sempre così. + +Alcune funzioni, come `strtolower()`, `strtoupper()`, e simili, nel recente passato hanno avuto un comportamento non deterministico e sono dipese dall'impostazione `setlocale()`. Questo ha causato molte complicazioni, soprattutto quando si lavorava con la lingua turca. +Questo perché la lingua turca distingue tra maiuscole e minuscole `I` con e senza punto. Quindi `strtolower('I')` restituiva il carattere `ı` e `strtoupper('i')` restituiva il carattere `İ`, il che portava le applicazioni a causare una serie di errori misteriosi. +Tuttavia, questo problema è stato risolto nella versione 8.2 di PHP e le funzioni non dipendono più dal locale. + +Questo è un bell'esempio di come lo stato globale abbia afflitto migliaia di sviluppatori in tutto il mondo. La soluzione è stata quella di sostituirlo con la dependency injection. + + +Quando è possibile utilizzare lo Stato globale? .[#toc-when-is-it-possible-to-use-global-state] +----------------------------------------------------------------------------------------------- + +Ci sono alcune situazioni specifiche in cui è possibile utilizzare lo stato globale. Ad esempio, quando si esegue il debug del codice ed è necessario scaricare il valore di una variabile o misurare la durata di una parte specifica del programma. In questi casi, che riguardano azioni temporanee che saranno successivamente rimosse dal codice, è legittimo utilizzare un dumper o un cronometro disponibile a livello globale. Questi strumenti non fanno parte della progettazione del codice. + +Un altro esempio sono le funzioni per lavorare con le espressioni regolari `preg_*`, che memorizzano internamente le espressioni regolari compilate in una cache statica in memoria. Quando si richiama la stessa espressione regolare più volte in diverse parti del codice, essa viene compilata una sola volta. La cache consente di risparmiare prestazioni ed è completamente invisibile all'utente, per cui tale utilizzo può essere considerato legittimo. + + +Sintesi .[#toc-summary] +----------------------- + +Abbiamo mostrato perché ha senso + +1) Rimuovere tutte le variabili statiche dal codice +2) Dichiarare le dipendenze +3) E utilizzare l'iniezione delle dipendenze + +Quando si progetta il codice, bisogna tenere presente che ogni `static $foo` rappresenta un problema. Affinché il codice sia un ambiente che rispetta la DI, è essenziale sradicare completamente lo stato globale e sostituirlo con la dependency injection. + +Durante questo processo, potreste scoprire che è necessario dividere una classe perché ha più di una responsabilità. Non preoccupatevi di questo; cercate di mantenere il principio di una sola responsabilità. + +*Vorrei ringraziare Miško Hevery, i cui articoli, come [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/], costituiscono la base di questo capitolo.* diff --git a/dependency-injection/it/introduction.texy b/dependency-injection/it/introduction.texy index ad2efec467..51ed1d6f54 100644 --- a/dependency-injection/it/introduction.texy +++ b/dependency-injection/it/introduction.texy @@ -406,10 +406,10 @@ interface Logger ```php class FileLogger implements Logger -//... +// ... class DatabaseLogger implements Logger -//... +// ... ``` In questo modo, non sarà necessario modificare nulla nel resto del codice in cui il logger viene utilizzato. Per esempio, il costruttore della classe `NewsletterDistributor` si accontenterà di richiedere `Logger` come parametro. E dipenderà da noi quale istanza passargli. diff --git a/dependency-injection/ja/@home.texy b/dependency-injection/ja/@home.texy index 276a697e30..0cafa9233a 100644 --- a/dependency-injection/ja/@home.texy +++ b/dependency-injection/ja/@home.texy @@ -5,8 +5,9 @@ 依存性注入は、コードや開発に対する見方を根本的に変えるデザインパターンです。クリーンな設計で持続可能なアプリケーションの世界への道を開くものです。 - [Dependency Injectionとは? |introduction] -- [DIコンテナとは? |container] +- [グローバルステートとシングルトン |global-state] - [依存性の受け渡し |passing-dependencies] +- [DIコンテナとは? |container] Nette DI diff --git a/dependency-injection/ja/@left-menu.texy b/dependency-injection/ja/@left-menu.texy index dac78fb0e6..0b2ac66703 100644 --- a/dependency-injection/ja/@left-menu.texy +++ b/dependency-injection/ja/@left-menu.texy @@ -1,8 +1,9 @@ 依存性注入 ***** - [DIとは? |introduction] -- [DI コンテナとは?|container] +- [グローバルステートとシングルトン |global-state] - [依存関係の受け渡し |passing-dependencies] +- [DI コンテナとは?|container] Nette DI diff --git a/dependency-injection/ja/global-state.texy b/dependency-injection/ja/global-state.texy new file mode 100644 index 0000000000..507095089c --- /dev/null +++ b/dependency-injection/ja/global-state.texy @@ -0,0 +1,312 @@ +グローバルステートとシングルトン +**************** + +.[perex] +警告:以下の構成要素は、コード設計が不十分な場合の症状です。 + +-`Foo::getInstance()` +-`DB::insert(...)` +-`Article::setDb($db)` +-`ClassName::$var` または`static::$var` + +あなたのコードに、これらの構成要素はありませんか?それなら、改善するチャンスがあります。これらは、さまざまなライブラリやフレームワークのサンプルソリューションでよく見かける構成だとお考えかもしれません。 +しかし、残念ながら、これらは設計が不十分であることを示す明確な指標となります。これらの共通点は、グローバルステートを使用していることです。 + +さて、私たちは確かに、ある種の学問的な純度の話をしているわけではありません。グローバルステートとシングルトンの使用は、コードの品質に破壊的な影響を及ぼします。その挙動は予測不可能になり、開発者の生産性を低下させ、クラス・インターフェースに真の依存関係を偽らせることになる。そして、プログラマーを混乱させます。 + +この章では、その方法を紹介します。 + + +グローバルインターリンキング .[#toc-global-interlinking] +------------------------------------------ + +グローバル状態の根本的な問題は、グローバルにアクセス可能であることです。そのため、グローバル(静的)メソッド`DB::insert()` を使ってデータベースへの書き込みが可能になってしまいます。 +理想的な世界では、オブジェクトは、[直接渡さ |passing-dependencies]れた他のオブジェクトとしか通信できないはずです。 +`A` と`B` の2つのオブジェクトを作成し、`A` から`B` に決して参照を渡さないとすると、`A` も`B` も、もう一方のオブジェクトにアクセスしたり、その状態を変更したりすることはできません。 +これは、コードの非常に望ましい機能です。これは、電池と電球があるのと似ていて、それらを一緒に配線しないと電球は点灯しないのです。 + +これは、グローバル(静的)変数やシングルトンには当てはまりません。`A` オブジェクトは、`C::changeSomething()` を呼び出すことで、*ワイヤレスで*`C` オブジェクトにアクセスし、参照を渡すことなく変更することができます。 +`B` オブジェクトがグローバルな`C` も把握している場合、`A` と`B` は`C` を介して互いに対話することができます。 + +グローバル変数の使用は、外からは見えない新しい形の*ワイヤレス*カップリングをシステムに導入します。 +これは、コードの理解や使用を複雑にする煙幕を作り出します。 +開発者は、依存関係を本当に理解するために、ソースコードのすべての行を読まなければなりません。単にクラスのインターフェイスに精通するのではなく、です。 +しかも、まったく不要なカップリングです。 + +.[note] +動作の面では、グローバル変数と静的変数の間に違いはありません。どちらも同じように有害です。 + + +遠距離の不気味な作用 .[#toc-the-spooky-action-at-a-distance] +-------------------------------------------------- + +1935年、アルベルト・アインシュタインは、量子物理学のある現象を「Spooky action at a distance」(距離による不気味な作用)と名付けた。 +量子もつれとは、ある粒子に関する情報を測定すると、たとえそれが何百万光年も離れていても、すぐに別の粒子に影響を与えるという特殊性のことである。 +これは、「光より速く移動するものはない」という宇宙の基本法則を一見破っているように見える。 + +ソフトウェアの世界では、(参照を渡していないので)孤立していると思われるプロセスを実行しても、オブジェクトに伝えていないシステムの遠い場所で予期せぬ相互作用や状態変化が起こる状況を「spooky action at a distance」と呼ぶことができます。これはグローバルな状態を通してのみ起こりうることです。 + +大規模で成熟したコードベースを持つプロジェクト開発チームに参加することを想像してください。新しいリーダーから新機能の実装を依頼されたあなたは、優秀な開発者らしく、テストを書くことから始めます。しかし、あなたはプロジェクトに参加したばかりなので、「このメソッドを呼び出したらどうなるか」という探索的なテストをたくさん行います。そして、次のようなテストを書こうとします。 + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // your card number + $cc->charge(100); +} +``` + +あなたはコードを実行し、おそらく数回実行しました。しばらくして、銀行からあなたの携帯電話に、実行するたびに100ドルがあなたのクレジットカードに請求されたという通知に気づきます 🤦‍♂️ + +一体どうやってテストで実際の請求が発生するのでしょうか?クレジットカードで操作するのは簡単ではありません。サードパーティのウェブサービスとやりとりしなければならない、そのウェブサービスのURLを知っていなければならない、ログインしなければならない、などなど。 +これらの情報は、テストには一切含まれていません。さらに悪いことに、この情報がどこに存在するのか、したがって、実行のたびに100ドルが再び請求されることがないように、外部の依存関係をどのように模擬すればよいのかさえもわからないのです。そして新米開発者であるあなたは、これからやろうとしていることが100ドル貧乏になることにつながると、どうやって知ることになるのでしょうか? + +遠目で見ると不気味な動作ですね!? + +プロジェクト内の接続の仕組みが理解できるまで、先輩や経験者に聞きながら、たくさんのソースコードを掘り下げるしかないのです。 +これは、`CreditCard` クラスのインターフェイスを見ても、初期化が必要なグローバル状態を判断できないことに起因しています。クラスのソースコードを見ても、どの初期化メソッドを呼び出せばいいのかがわからないのです。せいぜい、アクセスされているグローバル変数を見つけ、そこから初期化方法を推測するくらいです。 + +このようなプロジェクトのクラスは病的な嘘つきである。ペイメントカードは、インスタンス化して`charge()` メソッドを呼び出すだけでよいように装っています。しかし、それは密かに別のクラス、`PaymentGateway` と相互作用している。そのインターフェースでさえ、独立して初期化できると言っているが、実際には、ある設定ファイルからクレデンシャルを引き出したりするのである。 +このコードを書いた開発者には、`CreditCard` が`PaymentGateway` を必要とすることは明らかです。彼らはこのようにコードを書きました。しかし、このプロジェクトに初めて参加する人にとっては、これは完全な謎であり、学習の妨げになります。 + +どうすればこの状況を解決できるのか?簡単です。**Let the API declare dependencies.**(APIに依存関係を宣言させる)。 + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +コード内の関係が突然明らかになったことに注目してください。`charge()` メソッドが`PaymentGateway` を必要とすると宣言することで、このコードがどのように相互依存しているのか、誰かに尋ねる必要はありません。あなたは、このメソッドのインスタンスを作成しなければならないことを知っていて、それを実行しようとすると、アクセス・パラメータを提供しなければならないという事実にぶつかります。アクセス・パラメータがなければ、コードは実行すらできないのです。 + +そして最も重要なのは、決済ゲートウェイをモックにすることで、テストを実行するたびに100ドル請求されることがないようにしたことです。 + +グローバルな状態は、オブジェクトがAPIで宣言されていないものに密かにアクセスできるようになり、結果としてAPIを病的な嘘つきにしてしまいます。 + +あなたは今までこのように考えていなかったかもしれませんが、グローバルステートを使うときはいつも、秘密の無線通信チャンネルを作っているのです。不気味な遠隔操作によって、開発者は潜在的な相互作用を理解するためにコードのすべての行を読まなければならず、開発者の生産性を低下させ、新しいチームメンバーを混乱させる。 +あなたがコードを作成した人であれば、本当の依存関係を知っていますが、あなたの後に来る人は何も知りません。 + +グローバルな状態を使うようなコードを書かず、依存関係を渡すことを優先する。つまり、依存性注入です。 + + +グローバル国家の脆さ .[#toc-brittleness-of-the-global-state] +-------------------------------------------------- + +グローバルステートとシングルトンを使用するコードでは、そのステートがいつ、誰によって変更されたのか、決して確実ではありません。このリスクは、初期化時にすでに存在している。次のコードは、データベース接続を作成し、ペイメントゲートウェイを初期化することになっていますが、例外を投げ続け、その原因を見つけるのは非常に面倒です。 + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +`PaymentGateway` オブジェクトが他のオブジェクトに無線でアクセスし、その中にはデータベース接続を必要とするものがあることは、コードを詳しく見てみなければわかりません。したがって、`PaymentGateway` の前にデータベースを初期化する必要があります。しかし、グローバルステートという煙幕が、このことを隠しています。もし各クラスのAPIが嘘をつかず、依存関係を宣言していたら、どれだけの時間を節約できるでしょうか? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +データベース接続にグローバルアクセスを使用する場合にも、同様の問題が発生します。 + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +`save()` メソッドを呼び出す際、データベース接続がすでに作成されているかどうか、また、誰がその作成に責任を持つのかが不明確である。たとえば、テスト目的でデータベース接続をその場で変更したい場合、`DB::reconnect(...)` や`DB::reconnectForTest()` などの追加のメソッドを作成する必要があるでしょう。 + +一例を考えてみましょう。 + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +`$article->save()` を呼び出す際に、テストデータベースが本当に使用されていることをどこで確認できるのでしょうか?もし、`Foo::doSomething()` メソッドがグローバルデータベース接続を変更したとしたらどうでしょうか?それを知るためには、`Foo` クラスのソースコードと、おそらく他の多くのクラスのソースコードを調査する必要があります。しかし、この方法は短期的な答えしか得られません。なぜなら、将来的に状況が変わる可能性があるからです。 + +データベース接続を`Article` クラス内の静的変数に移したらどうでしょう。 + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +これでは全く何も変わりません。問題はグローバルな状態であり、どのクラスに潜んでいるかは関係ないのです。この場合、前のものと同様に、`$article->save()` メソッドが呼ばれたときに、どのデータベースに書き込まれているのかについては、全くわかりません。アプリケーションの遠くの端にいる誰もが、`Article::setDb()` を使っていつでもデータベースを変更することができます。私たちの手の中で + +グローバルな状態は、私たちのアプリケーションを**極めて壊れやすい**ものにしています。 + +しかし、この問題に対処する簡単な方法があります。APIに依存関係を宣言させるだけで、適切な機能を確保することができるのです。 + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +このアプローチにより、データベース接続の隠れた予期せぬ変更の心配がなくなります。今、私たちは記事がどこに保存されているかを確信しており、別の無関係なクラス内のコードを修正しても、もう状況を変えることはできません。コードはもはや壊れやすくなく、安定しているのです。 + +グローバルな状態を使うようなコードは書かないで、依存関係を渡す方がいい。したがって、依存性注入。 + + +シングルトン .[#toc-singleton] +------------------------ + +シングルトンは、有名なGang of Fourの出版物からの[定義により |https://en.wikipedia.org/wiki/Singleton_pattern]、クラスを単一のインスタンスに制限し、それに対してグローバルなアクセスを提供するデザインパターンである。このパターンの実装は、通常、次のようなコードに似ています。 + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // and other methods that perform the functions of the class +} +``` + +残念ながら、シングルトンはアプリケーションにグローバルな状態を導入することになります。そして、上で示したように、グローバルな状態は望ましくありません。これが、シングルトンがアンチパターンと言われる所以です。 + +コードにシングルトンを使わず、他のメカニズムに置き換えてください。シングルトンは本当に必要ない。しかし、アプリケーション全体に対して、あるクラスの単一のインスタンスの存在を保証する必要がある場合は、[DIコンテナに |container]任せます。 +したがって、アプリケーションシングルトン(サービス)を作成します。これにより、クラスは独自のユニークさを持たなくなり(つまり、`getInstance()` メソッドや静的変数を持たなくなり)、その機能のみを実行するようになります。したがって、単一責任の原則に違反することはなくなる。 + + +グローバル・ステート・バーズ・テスト .[#toc-global-state-versus-tests] +---------------------------------------------------- + +テストを書くとき、各テストは孤立したユニットであり、外部の状態が入り込むことはないと仮定します。また、テストから離れる状態もない。テストが完了すると、テストに関連する状態は、ガベージコレクタによって自動的に削除されるはずです。これにより、テストは孤立したものになります。したがって、テストを任意の順序で実行することができます。 + +しかし、グローバルな状態やシングルトンが存在する場合、これらの素敵な仮定はすべて崩れてしまいます。状態はテストに入り、テストから出ることができる。突然、テストの順番が問題になることがある。 + +シングルトンをテストするために、開発者はしばしば、インスタンスを別のものに置き換えるなどして、その特性を緩和しなければなりません。このような解決策は、せいぜいハック程度で、維持と理解が困難なコードを生成します。グローバルな状態に影響を与えるテストやメソッド(`tearDown()` )は、それらの変更を元に戻さなければなりません。 + +グローバルステートは、ユニットテストにおける最大の頭痛の種です + +どうすればこの状況を解決できるのか?簡単です。シングルトンを使うようなコードを書かず、依存関係を渡すことを優先する。つまり、依存性注入です。 + + +グローバル定数 .[#toc-global-constants] +-------------------------------- + +グローバルステートは、シングルトンや静的変数の使用に限らず、グローバル定数にも適用可能です。 + +定数の値が、新しい情報(`M_PI` )や有用な情報(`PREG_BACKTRACK_LIMIT_ERROR` )を提供しない定数は、明らかにOKです。 +逆に、コード内部で情報を*ワイヤレス*で受け渡す方法として機能する定数は、隠れた依存関係以外の何物でもありません。次の例の`LOG_FILE` のようなものです。 +`FILE_APPEND` 定数を使用することは完全に正しいです。 + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +この場合、`Foo` クラスのコンストラクタでパラメータを宣言し、API の一部とする必要があります。 + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +これで、ロギングファイルのパスに関する情報を渡して、必要に応じて簡単に変更できるようになり、コードのテストやメンテナンスがしやすくなりました。 + + +グローバルファンクションとスタティックメソッド .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------- + +静的メソッドやグローバル関数の使用自体が問題ではないことを強調したい。`DB::insert()` や同様のメソッドの使用が不適切であることを説明してきましたが、それは常に静的変数に格納されるグローバルな状態の問題でした。`DB::insert()` メソッドは、データベース接続を格納するため、静的変数の存在を必要とします。この変数がなければ、このメソッドを実装することは不可能です。 + +`DateTime::createFromFormat()`,`Closure::fromCallable`,`strlen()` などの決定論的な静的メソッドや関数の使用は、依存性注入と完全に一致します。これらの関数は、常に同じ入力パラメータから同じ結果を返すので、予測可能です。また、グローバルな状態を使用することもありません。 + +しかし、PHPには決定論的でない関数があります。例えば、`htmlspecialchars()` 関数がそうです。その第3パラメータである`$encoding` は、指定しない場合、デフォルトで設定オプション`ini_get('default_charset')` の値になります。したがって、関数の予測不可能な動作を避けるために、このパラメータを常に指定することが推奨されます。Netteでは一貫してこれを採用しています。 + +`strtolower()`,`strtoupper()` などの一部の関数は、最近になって非決定的な動作をするようになり、`setlocale()` の設定に依存するようになりました。このため、多くの複雑な問題が発生し、その多くはトルコ語を扱うときに発生しました。 +というのも、トルコ語はドットのある大文字と小文字`I` を区別しているからです。そのため、`strtolower('I')` は`ı` の文字を返し、`strtoupper('i')` は`İ` の文字を返します。このため、アプリケーションは多くの謎のエラーを引き起こすことになりました。 +しかし、この問題はPHPバージョン8.2で修正され、関数はロケールに依存しなくなりました。 + +これは、グローバルステートが世界中の何千人もの開発者を悩ませてきたことを示すいい例です。その解決策は、依存性注入に置き換えることでした。 + + +グローバルステートの使用はどのような場合に可能か? .[#toc-when-is-it-possible-to-use-global-state] +------------------------------------------------------------------------- + +グローバルステートを使用することが可能な特定の状況があります。例えば、コードをデバッグする際に、変数の値をダンプしたり、プログラムの特定の部分の時間を測定したりする必要がある場合です。このような場合、後でコードから削除される一時的な動作に関するものであれば、グローバルに利用可能なダンパやストップウォッチを使用することが正当です。これらのツールは、コード設計の一部ではありません。 + +もう一つの例は、正規表現を扱うための関数`preg_*` で、コンパイルされた正規表現を内部的にメモリ上の静的キャッシュに保存します。コードの異なる部分で同じ正規表現を複数回呼び出しても、コンパイルされるのは1回だけです。キャッシュは性能を節約し、またユーザーには全く見えないので、このような使い方は正当なものだと考えることができます。 + + +概要 .[#toc-summary] +------------------ + +なぜそれが理にかなっているのかを示しました + +1) コードからすべての静的変数を削除する +2) 依存関係を宣言する +3) そして依存性注入を使う + +コード設計を考えるとき、`static $foo` のそれぞれが問題を表していることに留意してください。あなたのコードがDIを尊重する環境になるためには、グローバルステートを完全に根絶し、依存性注入に置き換えることが必要不可欠です。 + +この過程で、クラスが複数の責任を持つため、クラスを分割する必要があることがわかるかもしれません。そのようなことは気にせず、1つの責任という原則を貫くようにしましょう。 + +*本章は、Miško Hevery氏の「[Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/]」等の論文に基づくものです。 diff --git a/dependency-injection/pl/@home.texy b/dependency-injection/pl/@home.texy index d47b7d35c3..ee15a7d396 100644 --- a/dependency-injection/pl/@home.texy +++ b/dependency-injection/pl/@home.texy @@ -5,8 +5,9 @@ Dependency Injection Dependency Injection jest wzorcem projektowym, który fundamentalnie zmieni sposób w jaki patrzysz na kod i rozwój. Otwiera on drogę do świata czysto zaprojektowanych i trwałych aplikacji. - [Co to jest Wstrzykiwanie Zależności? |introduction] -- [Co to jest kontener DI? |container] +- [Stan globalny i singletony |global-state] - [Przekazywanie zależności|passing-dependencies] +- [Co to jest kontener DI? |container] Nette DI diff --git a/dependency-injection/pl/@left-menu.texy b/dependency-injection/pl/@left-menu.texy index 68cc779c43..82f316ea45 100644 --- a/dependency-injection/pl/@left-menu.texy +++ b/dependency-injection/pl/@left-menu.texy @@ -1,8 +1,9 @@ Wtrysk zależności ***************** - [Co to jest DI? |introduction] -- [Co to jest kontener DI? |container] +- [Stan globalny i singletony |global-state] - [Przekazywanie zależności |passing-dependencies] +- [Co to jest kontener DI? |container] Nette DI diff --git a/dependency-injection/pl/global-state.texy b/dependency-injection/pl/global-state.texy new file mode 100644 index 0000000000..d44453c450 --- /dev/null +++ b/dependency-injection/pl/global-state.texy @@ -0,0 +1,312 @@ +Stan globalny i singletony +************************** + +.[perex] +Ostrzeżenie: poniższe konstrukcje są objawami złego projektowania kodu: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` lub `static::$var` + +Czy któryś z tych konstruktów występuje w Twoim kodzie? W takim razie masz okazję do poprawy. Możesz myśleć, że są to powszechne konstrukcje, które widzimy w przykładowych rozwiązaniach różnych bibliotek i frameworków. +Niestety, nadal są one wyraźnym wskaźnikiem złego projektu. Mają one jedną wspólną cechę: wykorzystanie stanu globalnego. + +Teraz z pewnością nie mówimy o jakiejś akademickiej czystości. Używanie globalnego stanu i singletonów ma destrukcyjny wpływ na jakość kodu. Jego zachowanie staje się nieprzewidywalne, zmniejsza produktywność programistów i zmusza interfejsy klas do kłamania na temat ich prawdziwych zależności. Co dezorientuje programistów. + +W tym rozdziale pokażemy, jak to jest możliwe. + + +Globalne powiązania .[#toc-global-interlinking] +----------------------------------------------- + +Podstawowy problem z globalnym stanem polega na tym, że jest on globalnie dostępny. Umożliwia to zapis do bazy danych poprzez globalną (statyczną) metodę `DB::insert()`. +W idealnym świecie obiekt powinien być w stanie komunikować się tylko z innymi obiektami, które zostały [bezpośrednio przekazane do |passing-dependencies] niego. +Jeśli stworzę dwa obiekty `A` i `B` i nigdy nie przekażę referencji z `A` do `B`, to ani `A`, ani `B` nie mogą uzyskać dostępu do drugiego obiektu ani zmienić jego stanu. +Jest to bardzo pożądana cecha kodu. Jest to podobne do posiadania baterii i żarówki; żarówka nie będzie świecić, dopóki nie połączysz ich przewodem. + +Nie jest to prawdą w przypadku zmiennych globalnych (statycznych) lub singletonów. Obiekt `A` mógłby *bezprzewodowo* uzyskać dostęp do obiektu `C` i zmodyfikować go bez przekazywania referencji, poprzez wywołanie `C::changeSomething()`. +Jeśli obiekt `B` chwyci również globalny `C`, to `A` i `B` mogą współdziałać ze sobą poprzez `C`. + +Użycie zmiennych globalnych wprowadza do systemu nową formę *bezprzewodowego* sprzężenia, która nie jest widoczna z zewnątrz. +Tworzy zasłonę dymną komplikującą zrozumienie i wykorzystanie kodu. +Programiści muszą czytać każdą linię kodu źródłowego, aby naprawdę zrozumieć zależności. Zamiast po prostu zapoznać się z interfejsem klas. +Co więcej, jest to całkowicie niepotrzebne sprzężenie. + +.[note] +Pod względem zachowania nie ma różnicy między zmienną globalną a statyczną. Są one równie szkodliwe. + + +Upiorne działanie na odległość .[#toc-the-spooky-action-at-a-distance] +---------------------------------------------------------------------- + +"Upiorne działanie na odległość" - tak Albert Einstein nazwał słynne zjawisko w fizyce kwantowej, które w 1935 roku przyprawiło go o dreszcze. +Chodzi o splątanie kwantowe, którego osobliwość polega na tym, że gdy mierzymy informację o jednej cząstce, natychmiast wpływamy na inną cząstkę, nawet jeśli są one oddalone od siebie o miliony lat świetlnych. +Co pozornie narusza podstawowe prawo wszechświata, że nic nie może podróżować szybciej niż światło. + +W świecie oprogramowania "upiornym działaniem na odległość" możemy nazwać sytuację, w której uruchamiamy proces, o którym myślimy, że jest izolowany (bo nie przekazaliśmy mu żadnych referencji), ale nieoczekiwane interakcje i zmiany stanu zachodzą w odległych miejscach systemu, o których nie powiedzieliśmy obiektowi. Może to nastąpić tylko poprzez stan globalny. + +Wyobraź sobie, że dołączasz do zespołu rozwijającego projekt, który ma dużą, dojrzałą bazę kodu. Twój nowy lider prosi cię o wdrożenie nowej funkcji i, jak dobry deweloper, zaczynasz od napisania testu. Ale ponieważ jesteś nowy w projekcie, robisz wiele testów eksploracyjnych "co się stanie, jeśli zadzwonię do tej metody" typu. I próbujesz napisać następujący test: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // numer karty + $cc->charge(100); +} +``` + +Uruchamiasz kod, może kilka razy, i po jakimś czasie zauważasz na swoim telefonie powiadomienia z banku, że przy każdym uruchomieniu 100$ zostało pobrane z Twojej karty kredytowej 🤦‍♂️ + +Jak u licha test mógł spowodować faktyczne obciążenie? Nie jest łatwo operować kartą kredytową. Musisz wejść w interakcję z usługą internetową strony trzeciej, musisz znać adres URL tej usługi internetowej, musisz się zalogować i tak dalej. +Żadna z tych informacji nie jest zawarta w teście. Co gorsza, nie wiesz nawet, gdzie te informacje są obecne, a zatem jak kpić z zewnętrznych zależności, aby każdy bieg nie powodował ponownego naliczania 100 USD. A jako nowy deweloper, jak miałeś wiedzieć, że to, co zamierzasz zrobić, doprowadzi do tego, że będziesz uboższy o 100 dolarów? + +To jest upiorne działanie na odległość! + +Nie masz wyboru, musisz przekopać się przez wiele kodu źródłowego, pytając starszych i bardziej doświadczonych kolegów, aż zrozumiesz, jak działają połączenia w projekcie. +Wynika to z faktu, że patrząc na interfejs klasy `CreditCard`, nie można określić stanu globalnego, który musi zostać zainicjalizowany. Nawet patrząc na kod źródłowy klasy nie dowiesz się, którą metodę inicjalizacji należy wywołać. W najlepszym przypadku możesz znaleźć zmienną globalną, do której uzyskuje się dostęp, i spróbować zgadnąć, jak ją zainicjalizować z tego. + +Klasy w takim projekcie są patologicznymi kłamcami. Karta płatnicza udaje, że można ją po prostu zainicjować i wywołać metodę `charge()`. Jednak potajemnie współdziała z inną klasą, `PaymentGateway`. Nawet jej interfejs mówi, że może być inicjalizowana niezależnie, ale w rzeczywistości ściąga poświadczenia z jakiegoś pliku konfiguracyjnego i tak dalej. +Dla programistów, którzy napisali ten kod, jest jasne, że `CreditCard` potrzebuje `PaymentGateway`. Napisali kod w ten sposób. Ale dla każdego nowego w projekcie jest to kompletna tajemnica i utrudnia naukę. + +Jak naprawić tę sytuację? Łatwo. **Pozwól API zadeklarować zależności**. + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Zauważ, jak zależności wewnątrz kodu są nagle oczywiste. Deklarując, że metoda `charge()` potrzebuje `PaymentGateway`, nie musisz nikogo pytać, w jaki sposób kod jest współzależny. Wiesz, że musisz stworzyć jego instancję, a kiedy próbujesz to zrobić, natrafiasz na fakt, że musisz dostarczyć parametry dostępu. Bez nich kod nawet by się nie uruchomił. + +I co najważniejsze, możesz teraz kpić z bramki płatności, więc nie zostaniesz obciążony 100 $ za każdym razem, gdy uruchomisz test. + +Stan globalny powoduje, że twoje obiekty mogą potajemnie uzyskać dostęp do rzeczy, które nie są zadeklarowane w ich interfejsach API, a w rezultacie czyni twoje interfejsy API patologicznymi kłamcami. + +Być może wcześniej nie myślałeś o tym w ten sposób, ale zawsze, gdy używasz stanu globalnego, tworzysz tajne kanały komunikacji bezprzewodowej. Przerażające zdalne działanie zmusza programistów do czytania każdej linijki kodu, aby zrozumieć potencjalne interakcje, zmniejsza produktywność programistów i dezorientuje nowych członków zespołu. +Jeśli jesteś tym, który stworzył kod, znasz prawdziwe zależności, ale każdy, kto przyjdzie po tobie, nie ma pojęcia. + +Nie pisz kodu, który używa globalnego stanu, wolą przekazać zależności. To jest zastrzyk zależności. + + +Kruchość globalnego państwa .[#toc-brittleness-of-the-global-state] +------------------------------------------------------------------- + +W kodzie, który używa stanu globalnego i singletonów, nigdy nie ma pewności, kiedy i przez kogo ten stan został zmieniony. To ryzyko pojawia się już podczas inicjalizacji. Poniższy kod ma za zadanie utworzyć połączenie z bazą danych i zainicjalizować bramkę płatniczą, ale ciągle rzuca wyjątek, a znalezienie przyczyny jest niezwykle żmudne: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Musisz szczegółowo przejrzeć kod, aby znaleźć, że obiekt `PaymentGateway` uzyskuje dostęp do innych obiektów bezprzewodowo, z których niektóre wymagają połączenia z bazą danych. Musisz więc zainicjalizować bazę danych przed `PaymentGateway`. Jednak zasłona dymna stanu globalnego ukrywa to przed tobą. Ile czasu zaoszczędziłbyś, gdyby API każdej klasy nie kłamało i nie deklarowało swoich zależności? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Podobny problem pojawia się podczas korzystania z globalnego dostępu do połączenia z bazą danych: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Podczas wywoływania metody `save()` nie ma pewności, czy połączenie z bazą danych zostało już utworzone i kto jest odpowiedzialny za jego utworzenie. Na przykład, gdybyśmy chcieli zmienić połączenie z bazą danych w locie, być może w celach testowych, prawdopodobnie musielibyśmy stworzyć dodatkowe metody, takie jak `DB::reconnect(...)` lub `DB::reconnectForTest()`. + +Rozważmy przykład: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Skąd możemy mieć pewność, że testowa baza danych jest naprawdę używana podczas wywoływania metody `$article->save()`? Co by było, gdyby metoda `Foo::doSomething()` zmieniła globalne połączenie z bazą danych? Aby się tego dowiedzieć, musielibyśmy zbadać kod źródłowy klasy `Foo` i prawdopodobnie wielu innych klas. Jednak takie podejście dałoby tylko krótkotrwałą odpowiedź, ponieważ sytuacja może się zmienić w przyszłości. + +Co by się stało, gdybyśmy przenieśli połączenie z bazą danych do zmiennej statycznej wewnątrz klasy `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +To w ogóle niczego nie zmienia. Problem jest stanem globalnym i nie ma znaczenia, w której klasie się ukrywa. W tym przypadku, podobnie jak w poprzednim, nie mamy pojęcia, do jakiej bazy danych jest zapisywana w momencie wywołania metody `$article->save()`. Ktokolwiek na odległym końcu aplikacji mógłby w każdej chwili zmienić bazę danych za pomocą `Article::setDb()`. Pod naszymi rękami. + +Stan globalny sprawia, że nasza aplikacja jest **ekstremalnie krucha**. + +Istnieje jednak prosty sposób na poradzenie sobie z tym problemem. Wystarczy kazać API zadeklarować zależności, aby zapewnić odpowiednią funkcjonalność. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Takie podejście eliminuje obawy o ukryte i nieoczekiwane zmiany w połączeniach z bazą danych. Teraz mamy pewność, gdzie przechowywany jest artykuł i żadne modyfikacje kodu wewnątrz innej niepowiązanej klasy nie mogą już zmienić sytuacji. Kod nie jest już kruchy, ale stabilny. + +Nie pisz kodu, który korzysta z globalnego stanu, wolisz przekazać zależności. A więc dependency injection. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton to wzorzec projektowy, który z [definicji |https://en.wikipedia.org/wiki/Singleton_pattern] ze słynnej publikacji Gang of Four ogranicza klasę do pojedynczej instancji i oferuje do niej globalny dostęp. Implementacja tego wzorca zazwyczaj przypomina następujący kod: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // oraz inne metody realizujące funkcje klasy +} +``` + +Niestety, singleton wprowadza do aplikacji stan globalny. A jak pokazaliśmy powyżej, stan globalny jest niepożądany. Dlatego właśnie singleton jest uważany za antypattern. + +Nie używaj singletonów w swoim kodzie i zastąp je innymi mechanizmami. Naprawdę nie potrzebujesz singletonów. Jeśli jednak musisz zagwarantować istnienie pojedynczej instancji klasy dla całej aplikacji, zostaw to [kontenerowi DI |container]. +W ten sposób utwórz singleton aplikacji, czyli usługę. Dzięki temu klasa przestanie zapewniać własną unikalność (tzn. Nie będzie miała metody `getInstance()` i zmiennej statycznej) i będzie wykonywać tylko swoje funkcje. Tym samym przestanie naruszać zasadę pojedynczej odpowiedzialności. + + +Stan globalny a testy .[#toc-global-state-versus-tests] +------------------------------------------------------- + +Pisząc testy, zakładamy, że każdy test jest izolowaną jednostką i że nie wchodzi do niego żaden zewnętrzny stan. I żaden stan nie opuszcza testów. Kiedy test się kończy, wszelkie stany związane z testem powinny być automatycznie usuwane przez garbage collector. To sprawia, że testy są odizolowane. Dlatego możemy uruchamiać testy w dowolnej kolejności. + +Jednakże, jeśli obecne są globalne stany/singletony, wszystkie te miłe założenia ulegają załamaniu. Stan może wejść i wyjść z testu. Nagle okazuje się, że kolejność wykonywania testów może mieć znaczenie. + +Aby w ogóle testować singletony, programiści często muszą rozluźnić ich właściwości, być może pozwalając na zastąpienie jednej instancji inną. Takie rozwiązania są w najlepszym wypadku hackami, które produkują kod trudny do utrzymania i zrozumienia. Każdy test lub metoda `tearDown()`, która wpływa na jakikolwiek stan globalny, musi cofnąć te zmiany. + +Globalny stan jest największym bólem głowy w testach jednostkowych! + +Jak naprawić tę sytuację? Proste. Nie pisz kodu, który używa singletonów, wolisz przekazywać zależności. Czyli dependency injection. + + +Stałe globalne .[#toc-global-constants] +--------------------------------------- + +Stan globalny nie jest ograniczony do używania singletonów i zmiennych statycznych, ale może również dotyczyć stałych globalnych. + +Stałe, których wartość nie dostarcza nam żadnych nowych (`M_PI`) lub użytecznych (`PREG_BACKTRACK_LIMIT_ERROR`) informacji są oczywiście OK. +I odwrotnie, stałe, które służą jako sposób na *bezprzewodowe* przekazywanie informacji wewnątrz kodu, są niczym więcej niż ukrytą zależnością. Jak `LOG_FILE` w poniższym przykładzie. +Używanie stałej `FILE_APPEND` jest całkowicie poprawne. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +W tym przypadku powinniśmy zadeklarować parametr w konstruktorze klasy `Foo`, aby uczynić go częścią interfejsu API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Teraz możemy przekazać informację o ścieżce do pliku logowania i łatwo ją zmienić w razie potrzeby, co ułatwi testowanie i utrzymanie kodu. + + +Funkcje globalne i metody statyczne .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------------------- + +Chcemy podkreślić, że używanie metod statycznych i funkcji globalnych samo w sobie nie jest problematyczne. Wyjaśniliśmy niewłaściwość używania `DB::insert()` i podobnych metod, ale zawsze chodziło o stan globalny przechowywany w zmiennej statycznej. Metoda `DB::insert()` wymaga istnienia zmiennej statycznej, ponieważ przechowuje ona połączenie z bazą danych. Bez tej zmiennej implementacja metody byłaby niemożliwa. + +Stosowanie deterministycznych metod i funkcji statycznych, takich jak `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` i wielu innych, jest doskonale zgodne z wstrzykiwaniem zależności. Funkcje te zawsze zwracają te same wyniki z tych samych parametrów wejściowych i dlatego są przewidywalne. Nie używają one żadnego stanu globalnego. + +W PHP istnieją jednak funkcje, które nie są deterministyczne. Należy do nich na przykład funkcja `htmlspecialchars()`. Jej trzeci parametr, `$encoding`, jeśli nie zostanie określony, domyślnie przyjmuje wartość opcji konfiguracyjnej `ini_get('default_charset')`. Dlatego zaleca się zawsze podawać ten parametr, aby uniknąć ewentualnego nieprzewidywalnego zachowania funkcji. Nette konsekwentnie to robi. + +Niektóre funkcje, takie jak `strtolower()`, `strtoupper()`, i tym podobne, miały w niedawnej przeszłości niedeterministyczne zachowanie i zależały od ustawienia `setlocale()`. Powodowało to wiele komplikacji, najczęściej podczas pracy z językiem tureckim. +Dzieje się tak dlatego, że język turecki rozróżnia duże i małe litery `I` z i bez kropki. Tak więc `strtolower('I')` zwracał znak `ı`, a `strtoupper('i')` zwracał znak `İ`, co prowadziło do aplikacji powodujących wiele tajemniczych błędów. +Jednak ten problem został naprawiony w PHP w wersji 8.2 i funkcje nie są już zależne od locale. + +Jest to ładny przykład tego, jak stan globalny nękał tysiące programistów na całym świecie. Rozwiązaniem było zastąpienie go zastrzykiem zależności. + + +Kiedy można użyć stanu globalnego? .[#toc-when-is-it-possible-to-use-global-state] +---------------------------------------------------------------------------------- + +Istnieją pewne specyficzne sytuacje, w których możliwe jest użycie stanu globalnego. Na przykład, gdy debugujemy kod i musimy zrzucić wartość zmiennej lub zmierzyć czas trwania określonej części programu. W takich przypadkach, które dotyczą działań tymczasowych, które później zostaną usunięte z kodu, uzasadnione jest użycie dostępnego globalnie dumpera lub stopera. Narzędzia te nie są częścią projektu kodu. + +Innym przykładem są funkcje do pracy z wyrażeniami regularnymi `preg_*`, które wewnętrznie przechowują skompilowane wyrażenia regularne w statycznej pamięci podręcznej w pamięci. Gdy wywołujesz to samo wyrażenie regularne wiele razy w różnych częściach kodu, jest ono kompilowane tylko raz. Cache oszczędza wydajność, a także jest całkowicie niewidoczny dla użytkownika, więc takie użycie można uznać za uzasadnione. + + +Podsumowanie .[#toc-summary] +---------------------------- + +Pokazaliśmy, dlaczego to ma sens + +1) Usuń z kodu wszystkie zmienne statyczne +2) Zadeklarować zależności +3) I używać zastrzyku zależności + +Rozważając projekt kodu, pamiętaj, że każdy `static $foo` reprezentuje problem. Aby twój kod był środowiskiem respektującym DI, konieczne jest całkowite wyeliminowanie stanu globalnego i zastąpienie go zastrzykiem zależności. + +Podczas tego procesu może się okazać, że musisz podzielić klasę, ponieważ ma ona więcej niż jedną odpowiedzialność. Nie przejmuj się tym; dąż do zasady jednej odpowiedzialności. + +*Chciałbym podziękować Miško Hevery'emu, którego artykuły takie jak [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] stanowią podstawę tego rozdziału.* diff --git a/dependency-injection/pt/@home.texy b/dependency-injection/pt/@home.texy index b4a81aaed2..3450b5eb1c 100644 --- a/dependency-injection/pt/@home.texy +++ b/dependency-injection/pt/@home.texy @@ -5,8 +5,9 @@ Injeção de dependência A injeção de dependência é um padrão de projeto que mudará fundamentalmente a maneira como você vê o código e o desenvolvimento. Ela abre o caminho para um mundo de aplicações sustentáveis e de design limpo. - [O que é Injeção de Dependência? |introduction] -- [O que é DI Container? |container] +- [Estado global e Singletons |global-state] - [Dependências de passagem |passing-dependencies] +- [O que é DI Container? |container] Nette DI diff --git a/dependency-injection/pt/@left-menu.texy b/dependency-injection/pt/@left-menu.texy index b17de0ee08..8e8801db45 100644 --- a/dependency-injection/pt/@left-menu.texy +++ b/dependency-injection/pt/@left-menu.texy @@ -1,8 +1,9 @@ Injeção de dependência ********************** - [O que é DI? |introduction] -- [O que é DI Container? |container] +- [Estado global e Singletons |global-state] - [Dependências de passagem |passing-dependencies] +- [O que é DI Container? |container] Nette DI diff --git a/dependency-injection/pt/global-state.texy b/dependency-injection/pt/global-state.texy new file mode 100644 index 0000000000..ca1f29c34a --- /dev/null +++ b/dependency-injection/pt/global-state.texy @@ -0,0 +1,312 @@ +Estado global e Singletons +************************** + +.[perex] +Advertência: as seguintes construções são sintomas de mau desenho de código: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` ou `static::$var` + +Alguma dessas construções ocorre em seu código? Então você tem uma oportunidade de melhorar. Você pode estar pensando que estas são construções comuns que vemos em exemplos de soluções de várias bibliotecas e estruturas. +Infelizmente, elas ainda são um claro indicador de mau projeto. Elas têm uma coisa em comum: o uso do estado global. + +Agora, certamente não estamos falando de algum tipo de pureza acadêmica. O uso do estado global e singletons tem efeitos destrutivos sobre a qualidade do código. Seu comportamento se torna imprevisível, reduz a produtividade do desenvolvedor e força as interfaces de classe a mentir sobre suas verdadeiras dependências. O que confunde os programadores. + +Neste capítulo, mostraremos como isto é possível. + + +Interligação global .[#toc-global-interlinking] +----------------------------------------------- + +O problema fundamental com o estado global é que ele é acessível globalmente. Isto torna possível escrever para o banco de dados através do método global (estático) `DB::insert()`. +Em um mundo ideal, um objeto só deve ser capaz de se comunicar com outros objetos que lhe tenham sido [diretamente passados |passing-dependencies]. +Se eu criar dois objetos `A` e `B` e nunca passar uma referência de `A` para `B`, então nem `A`, nem `B` podem acessar o outro objeto ou mudar seu estado. +Esta é uma característica muito desejável do código. É semelhante a ter uma bateria e uma lâmpada; a lâmpada não acenderá até que você as ligue juntas. + +Isto não é verdade para variáveis globais (estáticas) ou singletons. O objeto `A` poderia *sem fio* acessar o objeto `C` e modificá-lo sem passar por nenhuma referência, ligando para `C::changeSomething()`. +Se o objeto `B` também pegar o global `C`, então `A` e `B` podem interagir entre si através de `C`. + +O uso de variáveis globais introduz uma nova forma de acoplamento *sem fio* no sistema que não é visível do exterior. +Ele cria uma cortina de fumaça complicando a compreensão e o uso do código. +Os desenvolvedores devem ler cada linha de código fonte para compreender verdadeiramente as dependências. Ao invés de apenas se familiarizarem com a interface das classes. +Além disso, é um acoplamento completamente desnecessário. + +.[note] +Em termos de comportamento, não há diferença entre uma variável global e uma variável estática. Elas são igualmente prejudiciais. + + +A ação assustadora à distância .[#toc-the-spooky-action-at-a-distance] +---------------------------------------------------------------------- + +"Ação assustadora à distância" - é o que Albert Einstein chamou famoso fenômeno na física quântica que lhe deu arrepios em 1935. +É um emaranhado quântico, cuja peculiaridade é que quando você mede informações sobre uma partícula, você afeta imediatamente outra partícula, mesmo que elas estejam a milhões de anos-luz de distância. +o que aparentemente viola a lei fundamental do universo de que nada pode viajar mais rápido do que a luz. + +No mundo do software, podemos chamar uma "ação assustadora à distância" de uma situação em que executamos um processo que pensamos estar isolado (porque não passamos nenhuma referência), mas interações e mudanças de estado inesperadas acontecem em locais distantes do sistema, das quais não falamos ao objeto. Isto só pode acontecer através do estado global. + +Imagine se juntar a uma equipe de desenvolvimento de projetos que tenha uma base de código grande e madura. Sua nova liderança lhe pede para implementar uma nova funcionalidade e, como um bom desenvolvedor, você começa escrevendo um teste. Mas como você é novo no projeto, você faz um monte de testes exploratórios do tipo "o que acontece se eu chamar este método". E você tenta escrever o seguinte teste: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // o número de seu cartão + $cc->charge(100); +} +``` + +Você executa o código, talvez várias vezes, e depois de um tempo você nota em seu telefone notificações do banco de que cada vez que você o executa, $100 foram cobrados em seu cartão de crédito 🤦♂️ + +Como diabos o teste poderia causar uma carga real? Não é fácil de operar com cartão de crédito. Você tem que interagir com um serviço web de terceiros, você tem que conhecer o URL desse serviço web, você tem que fazer o login, e assim por diante. +Nenhuma destas informações está incluída no teste. Pior ainda, você nem sabe onde estas informações estão presentes e, portanto, como zombar das dependências externas para que cada execução não resulte na cobrança de US$ 100 novamente. E como um novo desenvolvedor, como você deveria saber que o que você estava prestes a fazer o levaria a ser $100 mais pobre? + +Isso é uma ação assustadora à distância! + +Você não tem escolha a não ser cavar muito código fonte, perguntando aos colegas mais velhos e mais experientes, até entender como funcionam as conexões no projeto. +Isto se deve ao fato de que, ao olhar para a interface da classe `CreditCard`, você não pode determinar o estado global que precisa ser inicializado. Mesmo olhando para o código-fonte da classe, você não dirá qual método de inicialização deve ser chamado. Na melhor das hipóteses, você pode encontrar a variável global a ser acessada e tentar adivinhar como inicializá-la a partir disso. + +As aulas em tal projeto são mentirosos patológicos. O cartão de pagamento finge que você pode simplesmente instanciá-lo e ligar para o método `charge()`. No entanto, ele interage secretamente com outra classe, `PaymentGateway`. Mesmo sua interface diz que pode ser inicializada independentemente, mas na realidade, ela retira credenciais de algum arquivo de configuração e assim por diante. +É claro para os desenvolvedores que escreveram este código que `CreditCard` precisa `PaymentGateway`. Eles escreveram o código desta forma. Mas para qualquer novato no projeto, isto é um mistério completo e dificulta o aprendizado. + +Como consertar a situação? Fácil. **Deixe a API declarar as dependências.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Observe como as relações dentro do código são subitamente óbvias. Ao declarar que o método `charge()` precisa `PaymentGateway`, você não precisa perguntar a ninguém como o código é interdependente. Você sabe que tem que criar uma instância dele, e quando você tenta fazer isso, você se depara com o fato de que tem que fornecer parâmetros de acesso. Sem eles, o código não funcionaria. + +E o mais importante, agora você pode zombar da porta de pagamento para que não lhe sejam cobrados 100 dólares cada vez que fizer um teste. + +O estado global faz com que seus objetos possam acessar secretamente coisas que não estão declaradas em seus APIs e, como resultado, torna seus APIs mentirosos patológicos. + +Você pode não ter pensado nisso antes, mas sempre que você usa o estado global, você está criando canais secretos de comunicação sem fio. A ação remota assustadora força os desenvolvedores a ler cada linha de código para entender as possíveis interações, reduz a produtividade dos desenvolvedores e confunde os novos membros da equipe. +Se foi você quem criou o código, você conhece as dependências reais, mas qualquer um que venha atrás de você não tem a menor idéia. + +Não escreva código que use o estado global, prefira passar dependências. Ou seja, injeção de dependência. + + +Brittleness do Estado Global .[#toc-brittleness-of-the-global-state] +-------------------------------------------------------------------- + +Em código que usa estado global e singletons, nunca é certo quando e por quem esse estado mudou. Este risco já está presente na inicialização. O código a seguir deve criar uma conexão de banco de dados e inicializar o gateway de pagamento, mas ele continua lançando uma exceção e encontrar a causa é extremamente enfadonho: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Você tem que percorrer o código em detalhes para descobrir que o objeto `PaymentGateway` acessa outros objetos sem fio, alguns dos quais requerem uma conexão de banco de dados. Portanto, você deve inicializar o banco de dados antes de `PaymentGateway`. No entanto, a cortina de fumaça do estado global esconde isso de você. Quanto tempo você economizaria se a API de cada classe não mentisse e declarasse suas dependências? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Um problema semelhante surge quando se utiliza o acesso global a uma conexão de banco de dados: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Ao chamar o método `save()`, não é certo se já foi criada uma conexão de banco de dados e quem é o responsável por criá-la. Por exemplo, se quiséssemos mudar a conexão de banco de dados na hora, talvez para fins de teste, provavelmente teríamos que criar métodos adicionais, como `DB::reconnect(...)` ou `DB::reconnectForTest()`. + +Considere um exemplo: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Onde podemos ter certeza de que o banco de dados de testes está realmente sendo usado quando se liga para `$article->save()`? E se o método `Foo::doSomething()` mudou a conexão global do banco de dados? Para descobrir, teríamos que examinar o código fonte da classe `Foo` e provavelmente muitas outras classes. Entretanto, esta abordagem forneceria apenas uma resposta a curto prazo, pois a situação pode mudar no futuro. + +E se movermos a conexão de banco de dados para uma variável estática dentro da classe `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Isto não muda absolutamente nada. O problema é um estado global e não importa em qual classe ele se esconde. Neste caso, como no anterior, não temos nenhuma pista de qual banco de dados está sendo escrito quando o método `$article->save()` é chamado. Qualquer pessoa no extremo distante da aplicação poderia alterar o banco de dados a qualquer momento usando `Article::setDb()`. Sob nossas mãos. + +O estado global torna nossa aplicação **extremamente frágil**. + +Entretanto, há uma maneira simples de lidar com este problema. Basta que o API declare as dependências para garantir a funcionalidade adequada. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Esta abordagem elimina a preocupação de mudanças ocultas e inesperadas nas conexões de banco de dados. Agora temos certeza de onde o artigo é armazenado e nenhuma modificação de código dentro de outra classe não relacionada pode mais alterar a situação. O código não é mais frágil, mas estável. + +Não escreva código que utilize o estado global, prefira passar dependências. Portanto, injeção de dependência. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton é um padrão de design que, por [definição |https://en.wikipedia.org/wiki/Singleton_pattern] da famosa publicação Gang of Four, restringe uma classe a uma única instância e oferece acesso global a ela. A implementação deste padrão geralmente se assemelha ao seguinte código: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // e outros métodos que desempenham as funções da classe +} +``` + +Infelizmente, o singleton introduz o estado global na aplicação. E como demonstramos acima, o estado global é indesejável. É por isso que o singleton é considerado um antipadrão. + +Não use singletons em seu código e substitua-os por outros mecanismos. Você realmente não precisa de singletons. Entretanto, se você precisar garantir a existência de uma única instância de uma classe para toda a aplicação, deixe-a para o [container DI |container]. +Assim, crie um singleton de aplicação, ou serviço. Isto impedirá a classe de fornecer sua própria singularidade (ou seja, ela não terá um método `getInstance()` e uma variável estática) e só desempenhará suas funções. Assim, deixará de violar o princípio da responsabilidade única. + + +Testes de estado global versus testes .[#toc-global-state-versus-tests] +----------------------------------------------------------------------- + +Ao escrever testes, assumimos que cada teste é uma unidade isolada e que nenhum estado externo entra nele. E que nenhum estado deixa os testes. Quando um teste é concluído, qualquer estado associado com o teste deve ser removido automaticamente pelo coletor de lixo. Isto faz com que os testes sejam isolados. Portanto, podemos executar os testes em qualquer ordem. + +Entretanto, se estados/cingilões globais estiverem presentes, todas essas simpáticas suposições se desmoronam. Um estado pode entrar e sair de um teste. De repente, a ordem dos testes pode ser importante. + +Para testar singletons, os desenvolvedores muitas vezes têm que relaxar suas propriedades, talvez permitindo que uma instância seja substituída por outra. Tais soluções são, na melhor das hipóteses, hacks que produzem códigos difíceis de manter e de entender. Qualquer teste ou método `tearDown()` que afete qualquer estado global deve desfazer essas mudanças. + +O estado global é a maior dor de cabeça em testes unitários! + +Como consertar a situação? Fácil. Não escreva código que utilize singletons, prefira passar dependências. Ou seja, injeção de dependência. + + +Constantes globais .[#toc-global-constants] +------------------------------------------- + +O estado global não se limita ao uso de singletons e variáveis estáticas, mas também pode se aplicar a constantes globais. + +Constantes cujo valor não nos fornece nenhuma informação nova (`M_PI`) ou útil (`PREG_BACKTRACK_LIMIT_ERROR`) são claramente OK. +Por outro lado, constantes que servem como uma forma de *sem fio* passar informações dentro do código nada mais são do que uma dependência oculta. Como `LOG_FILE` no exemplo a seguir. +O uso da constante `FILE_APPEND` é perfeitamente correto. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Neste caso, devemos declarar o parâmetro no construtor da classe `Foo` para torná-la parte da API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Agora podemos passar informações sobre o caminho para o arquivo de registro e facilmente alterá-lo conforme necessário, facilitando o teste e a manutenção do código. + + +Funções globais e métodos estáticos .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------------------- + +Queremos enfatizar que o uso de métodos estáticos e funções globais não é, por si só, problemático. Explicamos a inadequação do uso de `DB::insert()` e métodos similares, mas sempre foi uma questão de estado global armazenada em uma variável estática. O método `DB::insert()` requer a existência de uma variável estática porque armazena a conexão de banco de dados. Sem esta variável, seria impossível implementar o método. + +O uso de métodos e funções estáticas determinísticas, tais como `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` e muitas outras, é perfeitamente consistente com a injeção de dependência. Estas funções sempre retornam os mesmos resultados a partir dos mesmos parâmetros de entrada e são, portanto, previsíveis. Elas não utilizam nenhum estado global. + +No entanto, há funções no PHP que não são determinísticas. Estas incluem, por exemplo, a função `htmlspecialchars()`. Seu terceiro parâmetro, `$encoding`, se não for especificado, é o valor padrão da opção de configuração `ini_get('default_charset')`. Portanto, recomenda-se sempre especificar este parâmetro para evitar um possível comportamento imprevisível da função. A Nette faz isto de forma consistente. + +Algumas funções, tais como `strtolower()`, `strtoupper()`, e similares, tiveram um comportamento não determinístico no passado recente e dependeram da configuração `setlocale()`. Isto causou muitas complicações, na maioria das vezes quando se trabalha com o idioma turco. +Isto porque o idioma turco distingue entre maiúsculas e minúsculas `I` com e sem um ponto. Assim, `strtolower('I')` devolveu o caracter `ı` e `strtoupper('i')` devolveu o caracter `İ`, o que levou a aplicações que causaram uma série de erros misteriosos. +Entretanto, este problema foi corrigido na versão 8.2 do PHP e as funções não são mais dependentes do locale. + +Este é um bom exemplo de como o estado global tem atormentado milhares de desenvolvedores em todo o mundo. A solução foi substituí-lo por uma injeção de dependência. + + +Quando é possível usar o Estado Global? .[#toc-when-is-it-possible-to-use-global-state] +--------------------------------------------------------------------------------------- + +Existem certas situações específicas em que é possível utilizar o estado global. Por exemplo, quando se depura o código e é necessário descarregar o valor de uma variável ou medir a duração de uma parte específica do programa. Em tais casos, que dizem respeito a ações temporárias que serão posteriormente removidas do código, é legítimo usar um dumper ou cronômetro disponível globalmente. Estas ferramentas não fazem parte do projeto do código. + +Outro exemplo são as funções para trabalhar com expressões regulares `preg_*`, que armazenam internamente expressões regulares compiladas em um cache estático na memória. Quando você chama a mesma expressão regular várias vezes em diferentes partes do código, ela é compilada apenas uma vez. O cache economiza desempenho e também é completamente invisível para o usuário, de modo que tal uso pode ser considerado legítimo. + + +Sumário .[#toc-summary] +----------------------- + +Mostramos porque faz sentido + +1) Remover todas as variáveis estáticas do código +2) Declarar as dependências +3) E usar injeção de dependência + +Ao contemplar o projeto do código, tenha em mente que cada `static $foo` representa um problema. Para que seu código seja um ambiente respeitador do DI, é essencial erradicar completamente o estado global e substituí-lo por injeção de dependência. + +Durante este processo, você pode descobrir que precisa dividir uma classe porque ela tem mais de uma responsabilidade. Não se preocupe com isso; esforce-se pelo princípio de uma responsabilidade. + +*Gostaria de agradecer a Miško Hevery, cujos artigos como [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] formam a base deste capítulo.* diff --git a/dependency-injection/ro/@home.texy b/dependency-injection/ro/@home.texy index 8df66d51f5..aeac969e68 100644 --- a/dependency-injection/ro/@home.texy +++ b/dependency-injection/ro/@home.texy @@ -5,8 +5,9 @@ Injectarea dependenței Injectarea dependenței este un model de proiectare care va schimba fundamental modul în care priviți codul și dezvoltarea. Acesta deschide calea către o lume a aplicațiilor cu design curat și sustenabil. - [Ce este injecția de dependență? |introduction] -- [Ce este DI Container? |container] +- [Starea globală și singletonii |global-state] - [Transmiterea dependențelor |passing-dependencies] +- [Ce este DI Container? |container] Nette DI diff --git a/dependency-injection/ro/@left-menu.texy b/dependency-injection/ro/@left-menu.texy index 8d0bd5b8f0..2427a51b50 100644 --- a/dependency-injection/ro/@left-menu.texy +++ b/dependency-injection/ro/@left-menu.texy @@ -1,8 +1,9 @@ Injectarea dependenței ********************** - [Ce este DI? |introduction] -- [Ce este DI Container? |container] +- [Stare globală și singletoni |global-state] - [Transmiterea dependențelor |passing-dependencies] +- [Ce este DI Container? |container] Nette DI diff --git a/dependency-injection/ro/global-state.texy b/dependency-injection/ro/global-state.texy new file mode 100644 index 0000000000..d408ba28fe --- /dev/null +++ b/dependency-injection/ro/global-state.texy @@ -0,0 +1,312 @@ +Starea globală și singletoni +**************************** + +.[perex] +Atenție: următoarele construcții sunt simptome ale unei proiectări proaste a codului: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` sau `static::$var` + +Apare vreuna dintre aceste construcții în codul dumneavoastră? Atunci aveți ocazia de a vă îmbunătăți. Poate că vă gândiți că acestea sunt construcții comune pe care le vedem în soluțiile de exemplu ale diferitelor biblioteci și cadre. +Din nefericire, ele reprezintă totuși un indicator clar al unei proiectări deficitare. Ele au un lucru în comun: utilizarea stării globale. + +Acum, cu siguranță nu vorbim despre un fel de puritate academică. Utilizarea stării globale și a singletonilor are efecte distructive asupra calității codului. Comportamentul său devine imprevizibil, reduce productivitatea dezvoltatorilor și forțează interfețele de clasă să mintă în legătură cu adevăratele lor dependențe. Ceea ce îi derutează pe programatori. + +În acest capitol, vom arăta cum este posibil acest lucru. + + +Interconectarea globală .[#toc-global-interlinking] +--------------------------------------------------- + +Problema fundamentală a statului global este că acesta este accesibil la nivel global. Acest lucru face posibilă scrierea în baza de date prin intermediul metodei globale (statice) `DB::insert()`. +Într-o lume ideală, un obiect ar trebui să poată comunica doar cu alte obiecte care [i-au fost transmise direct |passing-dependencies]. +Dacă creez două obiecte `A` și `B` și nu transmit niciodată o referință de la `A` la `B`, atunci nici `A`, nici `B` nu pot accesa celălalt obiect sau schimba starea acestuia. +Aceasta este o caracteristică foarte dorită a codului. Este similar cu a avea o baterie și un bec; becul nu se va aprinde până când nu le conectați împreună. + +Acest lucru nu este valabil pentru variabilele globale (statice) sau singletone. Obiectul `A` ar putea să acceseze *fără fir* obiectul `C` și să îl modifice fără a trece nicio referință, prin apelarea `C::changeSomething()`. +Dacă obiectul `B` prinde și obiectul global `C`, atunci `A` și `B` pot interacționa unul cu celălalt prin intermediul `C`. + +Utilizarea variabilelor globale introduce o nouă formă de cuplare *fără fir* în sistem, care nu este vizibilă din exterior. +Aceasta creează o perdea de fum care complică înțelegerea și utilizarea codului. +Dezvoltatorii trebuie să citească fiecare linie de cod sursă pentru a înțelege cu adevărat dependențele. În loc să se familiarizeze doar cu interfața claselor. +În plus, este un cuplaj complet inutil. + +.[note] +În ceea ce privește comportamentul, nu există nicio diferență între o variabilă globală și una statică. Ele sunt la fel de dăunătoare. + + +Acțiunea înfricoșătoare la distanță .[#toc-the-spooky-action-at-a-distance] +--------------------------------------------------------------------------- + +"Acțiunea ciudată la distanță" - așa a numit Albert Einstein un fenomen din fizica cuantică care i-a dat fiori în 1935. +Este vorba despre entanglarea cuantică, a cărei particularitate este că atunci când măsori informații despre o particulă, afectezi imediat o altă particulă, chiar dacă acestea se află la milioane de ani lumină distanță. +Ceea ce aparent încalcă legea fundamentală a universului conform căreia nimic nu poate călători mai repede decât lumina. + +În lumea software-ului, putem numi "acțiune fantomatică la distanță" o situație în care rulăm un proces pe care îl considerăm izolat (deoarece nu i-am transmis nicio referință), dar interacțiuni neașteptate și schimbări de stare au loc în locații îndepărtate ale sistemului, despre care nu am informat obiectul. Acest lucru se poate întâmpla numai prin intermediul stării globale. + +Imaginați-vă că vă alăturați unei echipe de dezvoltare a unui proiect care are o bază de cod mare și matură. Noul dvs. șef vă cere să implementați o nouă caracteristică și, ca un bun dezvoltator, începeți prin a scrie un test. Dar, pentru că sunteți nou în proiect, faceți o mulțime de teste exploratorii de tipul "ce se întâmplă dacă apelez această metodă". Și încercați să scrieți următorul test: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // numărul cardului dvs. + $cc->charge(100); +} +``` + +Rulați codul, poate de mai multe ori, și după un timp observați notificări pe telefon de la bancă care vă anunță că, de fiecare dată când îl executați, 100 de dolari au fost debitați de pe cardul dvs. de credit 🤦‍♂️. + +Cum naiba a putut testul să provoace o încărcare reală? Nu este ușor de operat cu cardul de credit. Trebuie să interacționezi cu un serviciu web terț, trebuie să cunoști URL-ul acelui serviciu web, trebuie să te loghezi și așa mai departe. +Niciuna dintre aceste informații nu este inclusă în test. Chiar mai rău, nici măcar nu știți unde sunt prezente aceste informații și, prin urmare, nu știți cum să vă bateți joc de dependențele externe, astfel încât fiecare execuție să nu ducă la o nouă taxare de 100 de dolari. Și, în calitate de dezvoltator nou, de unde să știi că ceea ce urma să faci te va duce la o sărăcie de 100 de dolari? + +Aceasta este o acțiune înfricoșătoare la distanță! + +Nu ai altă soluție decât să scotocești prin mult cod sursă, întrebând colegi mai vechi și mai experimentați, până când înțelegi cum funcționează conexiunile din proiect. +Acest lucru se datorează faptului că, atunci când vă uitați la interfața clasei `CreditCard`, nu puteți determina starea globală care trebuie inițializată. Nici măcar dacă vă uitați la codul sursă al clasei nu vă va spune ce metodă de inițializare trebuie să apelați. În cel mai bun caz, puteți găsi variabila globală care este accesată și încercați să ghiciți cum să o inițializați pornind de la aceasta. + +Clasele dintr-un astfel de proiect sunt niște mincinoși patologici. Cardul de plată pretinde că puteți pur și simplu să îl instanți și să apelați metoda `charge()`. Cu toate acestea, ea interacționează în secret cu o altă clasă, `PaymentGateway`. Chiar și interfața sa spune că poate fi inițializată în mod independent, dar în realitate trage acreditările dintr-un fișier de configurare și așa mai departe. +Este clar pentru dezvoltatorii care au scris acest cod că `CreditCard` are nevoie de `PaymentGateway`. Aceștia au scris codul în acest fel. Dar pentru oricine este nou în proiect, acest lucru este un mister complet și împiedică învățarea. + +Cum se poate remedia situația? Ușor. **Lasă API-ul să declare dependențele.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Observați cum relațiile din cadrul codului sunt brusc evidente. Declarând că metoda `charge()` are nevoie de `PaymentGateway`, nu mai trebuie să întrebați pe nimeni cum este interdependent codul. Știți că trebuie să creați o instanță a acesteia, iar când încercați să faceți acest lucru, vă loviți de faptul că trebuie să furnizați parametri de acces. Fără aceștia, codul nici măcar nu ar funcționa. + +Și, cel mai important, acum puteți să vă bateți joc de gateway-ul de plată, astfel încât să nu fiți taxat cu 100 de dolari de fiecare dată când executați un test. + +Starea globală face ca obiectele dvs. să poată accesa în secret lucruri care nu sunt declarate în API-urile lor și, ca urmare, face ca API-urile dvs. să fie mincinoase patologice. + +Poate că nu v-ați gândit la asta până acum, dar ori de câte ori folosiți starea globală, creați canale secrete de comunicare fără fir. Acțiunile înfiorătoare de la distanță îi obligă pe dezvoltatori să citească fiecare linie de cod pentru a înțelege interacțiunile potențiale, reduc productivitatea dezvoltatorilor și îi derutează pe noii membri ai echipei. +Dacă tu ești cel care a creat codul, cunoști dependențele reale, dar oricine vine după tine nu știe nimic. + +Nu scrieți cod care utilizează starea globală, preferați să treceți dependențele. Adică injectarea dependențelor. + + +Bătălia statului global .[#toc-brittleness-of-the-global-state] +--------------------------------------------------------------- + +În codul care utilizează starea globală și singletonii, nu este niciodată sigur când și de către cine a fost schimbată acea stare. Acest risc este deja prezent la inițializare. Următorul cod ar trebui să creeze o conexiune la baza de date și să inițializeze gateway-ul de plată, dar continuă să arunce o excepție, iar găsirea cauzei este extrem de anevoioasă: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Trebuie să parcurgeți codul în detaliu pentru a descoperi că obiectul `PaymentGateway` accesează alte obiecte fără fir, dintre care unele necesită o conexiune la baza de date. Astfel, trebuie să inițializați baza de date înainte de `PaymentGateway`. Cu toate acestea, perdeaua de fum a statului global vă ascunde acest lucru. Cât timp ați economisi dacă API-ul fiecărei clase nu ar minți și nu și-ar declara dependențele? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +O problemă similară apare atunci când se utilizează accesul global la o conexiune la o bază de date: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Atunci când se apelează metoda `save()`, nu se știe cu siguranță dacă a fost deja creată o conexiune la baza de date și cine este responsabil pentru crearea acesteia. De exemplu, dacă am dori să modificăm din mers conexiunea la baza de date, poate în scopuri de testare, probabil că ar trebui să creăm metode suplimentare, cum ar fi `DB::reconnect(...)` sau `DB::reconnectForTest()`. + +Luați în considerare un exemplu: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +De unde putem fi siguri că baza de date de testare este într-adevăr utilizată atunci când apelăm `$article->save()`? Ce se întâmplă dacă metoda `Foo::doSomething()` a schimbat conexiunea globală la baza de date? Pentru a afla, ar trebui să examinăm codul sursă al clasei `Foo` și, probabil, al multor alte clase. Cu toate acestea, această abordare ar oferi doar un răspuns pe termen scurt, deoarece situația se poate schimba în viitor. + +Ce se întâmplă dacă mutăm conexiunea la baza de date într-o variabilă statică în interiorul clasei `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Acest lucru nu schimbă absolut nimic. Problema este o stare globală și nu contează în ce clasă se ascunde. În acest caz, ca și în cel precedent, nu avem niciun indiciu cu privire la baza de date în care se scrie atunci când este apelată metoda `$article->save()`. Oricine aflat la capătul îndepărtat al aplicației ar putea schimba baza de date în orice moment folosind `Article::setDb()`. În mâinile noastre. + +Starea globală face ca aplicația noastră să fie **extrem de fragilă**. + +Cu toate acestea, există o modalitate simplă de a rezolva această problemă. Este suficient ca API-ul să declare dependențele pentru a asigura o funcționalitate corespunzătoare. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Această abordare elimină grija modificărilor ascunse și neașteptate ale conexiunilor la baza de date. Acum suntem siguri unde este stocat articolul și nicio modificare de cod în interiorul unei alte clase fără legătură nu mai poate schimba situația. Codul nu mai este fragil, ci stabil. + +Nu scrieți cod care utilizează starea globală, preferați să treceți dependențele. Astfel, injecția de dependențe. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton este un model de proiectare care, prin [definiția |https://en.wikipedia.org/wiki/Singleton_pattern] din celebra publicație Gang of Four, limitează o clasă la o singură instanță și oferă acces global la aceasta. Implementarea acestui model seamănă, de obicei, cu următorul cod: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // și alte metode care îndeplinesc funcțiile clasei +} +``` + +Din păcate, singletonul introduce o stare globală în aplicație. Și, după cum am arătat mai sus, starea globală nu este de dorit. De aceea, singletonul este considerat un antipattern. + +Nu folosiți singletonii în codul dvs. și înlocuiți-i cu alte mecanisme. Chiar nu aveți nevoie de singletons. Cu toate acestea, dacă trebuie să garantați existența unei singure instanțe a unei clase pentru întreaga aplicație, lăsați acest lucru în seama [containerului DI |container]. +Astfel, creați un singleton de aplicație, sau serviciu. Acest lucru va împiedica clasa să își asigure propria unicitate (adică nu va avea o metodă `getInstance()` și o variabilă statică) și își va îndeplini doar funcțiile. Astfel, nu va mai încălca principiul responsabilității unice. + + +Starea globală față de teste .[#toc-global-state-versus-tests] +-------------------------------------------------------------- + +Atunci când scriem teste, presupunem că fiecare test este o unitate izolată și că nicio stare externă nu intră în el. Și nicio stare nu părăsește testele. Atunci când un test se finalizează, orice stare asociată cu testul ar trebui să fie eliminată automat de către garbage collector. Acest lucru face ca testele să fie izolate. Prin urmare, putem rula testele în orice ordine. + +Cu toate acestea, dacă sunt prezente stări globale/singletele globale, toate aceste presupuneri frumoase se prăbușesc. O stare poate intra și ieși dintr-un test. Dintr-o dată, ordinea testelor poate conta. + +Pentru a testa singletonii, dezvoltatorii trebuie adesea să relaxeze proprietățile acestora, poate permițând ca o instanță să fie înlocuită cu alta. Astfel de soluții sunt, în cel mai bun caz, hack-uri care produc un cod dificil de întreținut și de înțeles. Orice test sau metodă `tearDown()` care afectează orice stare globală trebuie să anuleze aceste modificări. + +Starea globală este cea mai mare bătaie de cap în testarea unitară! + +Cum se poate remedia situația? Ușor. Nu scrieți cod care folosește singletoni, preferați să treceți dependențele. Adică injecția de dependență. + + +Constante globale .[#toc-global-constants] +------------------------------------------ + +Starea globală nu se limitează la utilizarea singletonilor și a variabilelor statice, ci se poate aplica și constantelor globale. + +Constantele a căror valoare nu ne furnizează informații noi (`M_PI`) sau utile (`PREG_BACKTRACK_LIMIT_ERROR`) sunt în mod clar în regulă. +Dimpotrivă, constantele care servesc drept modalitate de a transmite *fără fir* informații în interiorul codului nu sunt altceva decât o dependență ascunsă. Cum ar fi `LOG_FILE` din exemplul următor. +Utilizarea constantei `FILE_APPEND` este perfect corectă. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +În acest caz, ar trebui să declarăm parametrul în constructorul clasei `Foo` pentru a-l face parte din API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Acum putem transmite informații despre calea către fișierul de logare și o putem modifica cu ușurință, după cum este necesar, facilitând testarea și întreținerea codului. + + +Funcții globale și metode statice .[#toc-global-functions-and-static-methods] +----------------------------------------------------------------------------- + +Dorim să subliniem faptul că utilizarea metodelor statice și a funcțiilor globale nu este în sine problematică. Am explicat caracterul nepotrivit al utilizării `DB::insert()` și a metodelor similare, dar întotdeauna a fost vorba de starea globală stocată într-o variabilă statică. Metoda `DB::insert()` necesită existența unei variabile statice, deoarece stochează conexiunea la baza de date. Fără această variabilă, ar fi imposibil de implementat metoda. + +Utilizarea metodelor și funcțiilor statice deterministe, cum ar fi `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` și multe altele, este perfect coerentă cu injecția de dependență. Aceste funcții returnează întotdeauna aceleași rezultate la aceiași parametri de intrare și, prin urmare, sunt previzibile. Ele nu utilizează nicio stare globală. + +Cu toate acestea, există funcții în PHP care nu sunt deterministe. Printre acestea se numără, de exemplu, funcția `htmlspecialchars()`. Cel de-al treilea parametru al acesteia, `$encoding`, dacă nu este specificat, este valoarea implicită a opțiunii de configurare `ini_get('default_charset')`. Prin urmare, se recomandă să specificați întotdeauna acest parametru pentru a evita un eventual comportament imprevizibil al funcției. Nette face acest lucru în mod constant. + +Unele funcții, cum ar fi `strtolower()`, `strtoupper()`, și altele similare, au avut un comportament nedeterminist în trecutul recent și au depins de setarea `setlocale()`. Acest lucru a cauzat multe complicații, cel mai adesea atunci când se lucra cu limba turcă. +Acest lucru se datorează faptului că limba turcă face distincție între majuscule și minuscule `I` cu și fără punct. Astfel, `strtolower('I')` returna caracterul `ı`, iar `strtoupper('i')` returna caracterul `İ`, ceea ce a dus la aplicații care provocau o serie de erori misterioase. +Cu toate acestea, această problemă a fost rezolvată în versiunea 8.2 a PHP, iar funcțiile nu mai depind de locale. + +Acesta este un exemplu frumos al modului în care statul global a afectat mii de dezvoltatori din întreaga lume. Soluția a fost înlocuirea acesteia cu injecția de dependență. + + +Când este posibil să se utilizeze statul global? .[#toc-when-is-it-possible-to-use-global-state] +------------------------------------------------------------------------------------------------ + +Există anumite situații specifice în care este posibil să se utilizeze starea globală. De exemplu, atunci când depanați codul și trebuie să descărcați valoarea unei variabile sau să măsurați durata unei anumite părți a programului. În astfel de cazuri, care se referă la acțiuni temporare care vor fi ulterior eliminate din cod, este legitim să se utilizeze un dumper sau un cronometru disponibil la nivel global. Aceste instrumente nu fac parte din proiectarea codului. + +Un alt exemplu este reprezentat de funcțiile de lucru cu expresii regulate `preg_*`, care stochează intern expresiile regulate compilate într-o memorie cache statică în memorie. Atunci când apelați aceeași expresie regulată de mai multe ori în diferite părți ale codului, aceasta este compilată o singură dată. Memoria cache economisește performanță și este, de asemenea, complet invizibilă pentru utilizator, astfel încât o astfel de utilizare poate fi considerată legitimă. + + +Rezumat .[#toc-summary] +----------------------- + +Am arătat de ce are sens + +1) Să eliminăm toate variabilele statice din cod +2) Declarați dependențele +3) Și folosiți injectarea dependențelor + +Atunci când vă gândiți la proiectarea codului, nu uitați că fiecare `static $foo` reprezintă o problemă. Pentru ca codul dumneavoastră să fie un mediu care respectă DI, este esențial să eradicați complet starea globală și să o înlocuiți cu injecția de dependență. + +În timpul acestui proces, s-ar putea să descoperiți că trebuie să divizați o clasă deoarece aceasta are mai multe responsabilități. Nu vă faceți griji în această privință; străduiți-vă să respectați principiul unei singure responsabilități. + +*Doresc să îi mulțumesc lui Miško Hevery, ale cărui articole, precum [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/], constituie baza acestui capitol.* diff --git a/dependency-injection/ru/@home.texy b/dependency-injection/ru/@home.texy index 82fb1a2649..20db763d08 100644 --- a/dependency-injection/ru/@home.texy +++ b/dependency-injection/ru/@home.texy @@ -5,8 +5,9 @@ Dependency Injection - это паттерн проектирования, который в корне изменит ваш взгляд на код и разработку. Он открывает путь в мир чистых и устойчивых приложений. - [Что такое инжекция зависимостей? |introduction] -- [Что такое DI-контейнер? |container] +- [Глобальное состояние и синглтоны |global-state] - [Передача зависимостей |passing-dependencies] +- [Что такое DI-контейнер? |container] Nette DI diff --git a/dependency-injection/ru/@left-menu.texy b/dependency-injection/ru/@left-menu.texy index 83c03d4772..311d6af8ff 100644 --- a/dependency-injection/ru/@left-menu.texy +++ b/dependency-injection/ru/@left-menu.texy @@ -1,8 +1,9 @@ Внедрение зависимостей ********************** - [Что такое «внедрение зависимостей»? |introduction] -- [Что такое «DI-контейнер»? |container] +- [Глобальное состояние и синглтоны |global-state] - [Передача зависимостей |passing-dependencies] +- [Что такое «DI-контейнер»? |container] Nette DI diff --git a/dependency-injection/ru/global-state.texy b/dependency-injection/ru/global-state.texy new file mode 100644 index 0000000000..5300272f92 --- /dev/null +++ b/dependency-injection/ru/global-state.texy @@ -0,0 +1,312 @@ +Глобальное состояние и синглтоны +******************************** + +.[perex] +Предупреждение: следующие конструкции являются симптомами плохого дизайна кода: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` или `static::$var` + +Встречаются ли в вашем коде какие-либо из этих конструкций? Тогда у вас есть возможность улучшиться. Возможно, вы думаете, что это обычные конструкции, которые мы видим в примерах решений различных библиотек и фреймворков. +К сожалению, они все еще являются четким индикатором плохого дизайна. У них есть одна общая черта: использование глобального состояния. + +Мы, конечно, не говорим о какой-то академической чистоте. Использование глобального состояния и синглтонов разрушительно сказывается на качестве кода. Его поведение становится непредсказуемым, снижает производительность разработчиков и заставляет интерфейсы классов лгать о своих истинных зависимостях. Что сбивает программистов с толку. + +В этой главе мы покажем, как это возможно. + + +Глобальное сцепление .[#toc-global-interlinking] +------------------------------------------------ + +Фундаментальная проблема глобального состояния заключается в том, что оно глобально доступно. Это делает возможным запись в базу данных через глобальный (статический) метод `DB::insert()`. +В идеальном мире объект должен иметь возможность взаимодействовать только с другими объектами, которые были ему [непосредственно переда |passing-dependencies] ны. +Если я создам два объекта `A` и `B` и никогда не передам ссылку с `A` на `B`, то ни `A`, ни `B` не смогут получить доступ к другому объекту или изменить его состояние. +Это очень желательная особенность кода. Это похоже на наличие батарейки и лампочки; лампочка не загорится, пока вы не соедините их вместе. + +Это не относится к глобальным (статическим) переменным или синглтонам. Объект `A` может *без проводов* получить доступ к объекту `C` и изменить его без передачи ссылки, вызвав `C::changeSomething()`. +Если объект `B` также получает доступ к глобальной `C`, то `A` и `B` могут взаимодействовать друг с другом через `C`. + +Использование глобальных переменных вводит в систему новую форму *беспроводной* связи, которая не видна извне. +Это создает дымовую завесу, усложняющую понимание и использование кода. +Разработчики должны читать каждую строчку исходного кода, чтобы действительно понять зависимости. Вместо того чтобы просто ознакомиться с интерфейсом классов. +Более того, это совершенно ненужное сцепление. + +.[note] +С точки зрения поведения, нет никакой разницы между глобальной и статической переменной. Они одинаково вредны. + + +Зловещее действие на расстоянии .[#toc-the-spooky-action-at-a-distance] +----------------------------------------------------------------------- + +"Зловещее действие на расстоянии" - так Альберт Эйнштейн в 1935 году назвал явление в квантовой физике, от которого у него мурашки по коже. +Это квантовая запутанность, особенность которой заключается в том, что когда вы измеряете информацию об одной частице, вы немедленно воздействуете на другую частицу, даже если они находятся на расстоянии миллионов световых лет друг от друга. +Это, казалось бы, нарушает фундаментальный закон Вселенной, согласно которому ничто не может двигаться быстрее света. + +В мире программного обеспечения мы можем назвать "spooky action at a distance" ситуацию, когда мы запускаем процесс, который, как нам кажется, изолирован (потому что мы не передавали ему никаких ссылок), но неожиданные взаимодействия и изменения состояния происходят в удаленных местах системы, о которых мы не сообщили объекту. Это может произойти только через глобальное состояние. + +Представьте, что вы присоединились к команде разработчиков проекта, которая имеет большую, зрелую кодовую базу. Ваш новый руководитель просит вас реализовать новую функцию, и, как хороший разработчик, вы начинаете с написания теста. Но поскольку вы новичок в проекте, вы делаете много исследовательских тестов типа "что произойдет, если я вызову этот метод". И вы пытаетесь написать следующий тест: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // номер вашей карты + $cc->charge(100); +} +``` + +Вы запускаете код, возможно, несколько раз, и через некоторое время замечаете на своем телефоне уведомления от банка о том, что при каждом запуске с вашей кредитной карты было списано $100 🤦‍♂️. + +Как тест мог вызвать фактическое списание средств? С кредитной картой не так-то просто работать. Вы должны взаимодействовать с веб-службой третьей стороны, вы должны знать URL этой веб-службы, вы должны войти в систему и так далее. +Ни одна из этих сведений не включена в тест. Хуже того, вы даже не знаете, где эта информация присутствует, и, следовательно, как высмеять внешние зависимости, чтобы каждый прогон не приводил к повторному начислению $100. А как начинающий разработчик, откуда вы могли знать, что то, что вы собираетесь сделать, приведет к тому, что вы станете беднее на 100 долларов? + +Это жуткое действие на расстоянии! + +У вас нет другого выбора, кроме как копаться в большом количестве исходного кода, спрашивая старших и более опытных коллег, пока вы не поймете, как работают связи в проекте. +Это связано с тем, что, глядя на интерфейс класса `CreditCard`, вы не можете определить глобальное состояние, которое необходимо инициализировать. Даже просмотр исходного кода класса не подскажет вам, какой метод инициализации нужно вызвать. В лучшем случае, вы можете найти глобальную переменную, к которой обращаются, и попытаться угадать, как ее инициализировать, исходя из этого. + +Классы в таком проекте - патологические лжецы. Платежная карта делает вид, что вы можете просто инстанцировать ее и вызвать метод `charge()`. Однако она тайно взаимодействует с другим классом, `PaymentGateway`. Даже его интерфейс говорит, что он может быть инициализирован самостоятельно, но на самом деле он берет учетные данные из какого-то конфигурационного файла и так далее. +Разработчикам, написавшим этот код, ясно, что `CreditCard` нуждается в `PaymentGateway`. Они написали код таким образом. Но для новичков это полная загадка и мешает обучению. + +Как исправить ситуацию? Легко. **Пусть API объявляет зависимости.**. + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Обратите внимание, как внезапно стали очевидны взаимосвязи внутри кода. Объявив, что метод `charge()` нуждается в методе `PaymentGateway`, вам не нужно никого спрашивать о взаимозависимости кода. Вы знаете, что вам нужно создать его экземпляр, и когда вы пытаетесь это сделать, вы сталкиваетесь с тем, что вам нужно предоставить параметры доступа. Без них код даже не запустится. + +И самое главное, теперь вы можете поиздеваться над платежным шлюзом, чтобы с вас не снимали 100 долларов каждый раз, когда вы запускаете тест. + +Глобальное состояние заставляет ваши объекты тайно получать доступ к тому, что не объявлено в их API, и в результате делает ваши API патологическими лжецами. + +Возможно, вы не думали об этом раньше, но всякий раз, когда вы используете глобальное состояние, вы создаете секретные беспроводные каналы связи. Жуткие удаленные действия заставляют разработчиков читать каждую строчку кода, чтобы понять потенциальное взаимодействие, снижают производительность разработчиков и сбивают с толку новых членов команды. +Если вы один из тех, кто создал код, вы знаете реальные зависимости, но все, кто приходит после вас, ничего не знают. + +Не пишите код, который использует глобальное состояние, предпочитая передавать зависимости. Это и есть инъекция зависимостей. + + +Хрупкость глобального государства .[#toc-brittleness-of-the-global-state] +------------------------------------------------------------------------- + +В коде, использующем глобальное состояние и синглтоны, никогда нельзя точно сказать, когда и кем это состояние было изменено. Этот риск присутствует уже при инициализации. Следующий код должен создать соединение с базой данных и инициализировать платежный шлюз, но он постоянно выбрасывает исключение, и поиск причины крайне утомителен: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Приходится подробно изучать код, чтобы обнаружить, что объект `PaymentGateway` обращается к другим объектам по беспроводной связи, некоторые из которых требуют подключения к базе данных. Таким образом, вы должны инициализировать базу данных перед `PaymentGateway`. Однако дымовая завеса глобального состояния скрывает это от вас. Сколько времени вы бы сэкономили, если бы API каждого класса не лгал и не объявлял о своих зависимостях? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Аналогичная проблема возникает при использовании глобального доступа к соединению с базой данных: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +При вызове метода `save()` неясно, было ли уже создано соединение с базой данных и кто отвечает за его создание. Например, если бы мы захотели изменить соединение с базой данных "на лету", возможно, в целях тестирования, нам, вероятно, пришлось бы создать дополнительные методы, такие как `DB::reconnect(...)` или `DB::reconnectForTest()`. + +Рассмотрим пример: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Где мы можем быть уверены, что при вызове `$article->save()` действительно используется тестовая база данных? Что если метод `Foo::doSomething()` изменит глобальное подключение к базе данных? Чтобы выяснить это, нам придется изучить исходный код класса `Foo` и, вероятно, многих других классов. Однако такой подход даст лишь краткосрочный ответ, поскольку в будущем ситуация может измениться. + +Что если мы перенесем соединение с базой данных в статическую переменную внутри класса `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Это совершенно ничего не изменит. Проблема является глобальным состоянием, и не имеет значения, в каком классе она скрывается. В этом случае, как и в предыдущем, у нас нет никакой информации о том, в какую базу данных производится запись, когда вызывается метод `$article->save()`. Любой человек на удаленном конце приложения может в любой момент изменить базу данных с помощью `Article::setDb()`. Под нашими руками. + +Глобальное состояние делает наше приложение **очень хрупким**. + +Однако есть простой способ решить эту проблему. Просто попросите API объявить зависимости для обеспечения надлежащей функциональности. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Такой подход устраняет беспокойство о скрытых и неожиданных изменениях соединений с базой данных. Теперь мы точно знаем, где хранится статья, и никакие модификации кода внутри другого несвязанного класса уже не смогут изменить ситуацию. Код больше не хрупкий, а стабильный. + +Не пишите код, использующий глобальное состояние, предпочитайте передавать зависимости. Таким образом, инъекция зависимостей. + + +Синглтон .[#toc-singleton] +-------------------------- + +Синглтон - это паттерн проектирования, который, по [определению |https://en.wikipedia.org/wiki/Singleton_pattern] из знаменитой публикации Gang of Four, ограничивает класс одним экземпляром и предоставляет к нему глобальный доступ. Реализация этого паттерна обычно похожа на следующий код: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // и другие методы, выполняющие функции класса +} +``` + +К сожалению, синглтон вносит глобальное состояние в приложение. А как мы показали выше, глобальное состояние нежелательно. Вот почему синглтон считается антипаттерном. + +Не используйте синглтоны в своем коде и замените их другими механизмами. Вам действительно не нужны синглтоны. Однако если вам нужно гарантировать существование единственного экземпляра класса для всего приложения, предоставьте это [DI-контейнеру |container]. +Таким образом, создайте синглтон приложения, или сервис. В результате класс перестанет обеспечивать собственную уникальность (т.е. у него не будет метода `getInstance()` и статической переменной) и будет выполнять только свои функции. Таким образом, он перестанет нарушать принцип единой ответственности. + + +Глобальное состояние против тестов .[#toc-global-state-versus-tests] +-------------------------------------------------------------------- + +При написании тестов мы предполагаем, что каждый тест является изолированной единицей и никакое внешнее состояние в него не попадает. И никакое состояние не покидает тесты. Когда тест завершается, любое состояние, связанное с тестом, должно быть автоматически удалено сборщиком мусора. Это делает тесты изолированными. Поэтому мы можем запускать тесты в любом порядке. + +Однако если присутствуют глобальные состояния/синглтоны, все эти приятные предположения разрушаются. Состояние может войти в тест и выйти из него. Неожиданно порядок выполнения тестов может иметь значение. + +Чтобы вообще тестировать синглтоны, разработчикам часто приходится ослаблять их свойства, возможно, позволяя заменять один экземпляр другим. Такие решения в лучшем случае являются хаками, которые создают код, который трудно поддерживать и понимать. Любой тест или метод `tearDown()`, который влияет на любое глобальное состояние, должен отменить эти изменения. + +Глобальное состояние - это самая большая головная боль в модульном тестировании! + +Как исправить ситуацию? Легко. Не пишите код, использующий синглтоны, предпочитайте передавать зависимости. То есть, инъекция зависимостей. + + +Глобальные константы .[#toc-global-constants] +--------------------------------------------- + +Глобальное состояние не ограничивается использованием синглтонов и статических переменных, но также может применяться к глобальным константам. + +Константы, значение которых не предоставляет нам никакой новой (`M_PI`) или полезной (`PREG_BACKTRACK_LIMIT_ERROR`) информации, явно не являются нормальными. +И наоборот, константы, которые служат способом *беспроводной* передачи информации внутри кода, являются ничем иным, как скрытой зависимостью. Например, `LOG_FILE` в следующем примере. +Использование константы `FILE_APPEND` совершенно корректно. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +В этом случае мы должны объявить параметр в конструкторе класса `Foo`, чтобы сделать его частью API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Теперь мы можем передавать информацию о пути к файлу протоколирования и легко изменять его по мере необходимости, что облегчает тестирование и сопровождение кода. + + +Глобальные функции и статические методы .[#toc-global-functions-and-static-methods] +----------------------------------------------------------------------------------- + +Мы хотим подчеркнуть, что использование статических методов и глобальных функций само по себе не является проблематичным. Мы уже объясняли неуместность использования `DB::insert()` и подобных методов, но это всегда было связано с глобальным состоянием, хранящимся в статической переменной. Метод `DB::insert()` требует существования статической переменной, поскольку в ней хранится соединение с базой данных. Без этой переменной реализовать метод было бы невозможно. + +Использование детерминированных статических методов и функций, таких как `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` и многих других, полностью соответствует инъекции зависимостей. Эти функции всегда возвращают одни и те же результаты при одних и тех же входных параметрах и поэтому предсказуемы. Они не используют никакого глобального состояния. + +Однако в PHP есть функции, которые не являются детерминированными. К ним относится, например, функция `htmlspecialchars()`. Ее третий параметр, `$encoding`, если не указан, по умолчанию принимает значение параметра конфигурации `ini_get('default_charset')`. Поэтому рекомендуется всегда указывать этот параметр, чтобы избежать возможного непредсказуемого поведения функции. Nette последовательно делает это. + +Некоторые функции, такие как `strtolower()`, `strtoupper()`, и тому подобные, в недавнем прошлом имели недетерминированное поведение и зависели от параметра `setlocale()`. Это вызывало множество осложнений, чаще всего при работе с турецким языком. +Это связано с тем, что турецкий язык различает верхний и нижний регистр `I` с точкой и без точки. Таким образом, `strtolower('I')` возвращал символ `ı`, а `strtoupper('i')` - символ `İ`, что приводило к тому, что приложения выдавали ряд загадочных ошибок. +Однако эта проблема была исправлена в PHP версии 8.2, и функции больше не зависят от локали. + +Это наглядный пример того, как глобальное состояние мучает тысячи разработчиков по всему миру. Решением было заменить его инъекцией зависимостей. + + +Когда можно использовать глобальное состояние? .[#toc-when-is-it-possible-to-use-global-state] +---------------------------------------------------------------------------------------------- + +Существуют определенные ситуации, когда можно использовать глобальное состояние. Например, при отладке кода, когда вам нужно сбросить значение переменной или измерить длительность определенной части программы. В таких случаях, когда речь идет о временных действиях, которые впоследствии будут удалены из кода, вполне законно использовать глобально доступный дампер или секундомер. Эти инструменты не являются частью дизайна кода. + +Другой пример - функции для работы с регулярными выражениями `preg_*`, которые внутренне хранят скомпилированные регулярные выражения в статическом кэше в памяти. Когда вы вызываете одно и то же регулярное выражение несколько раз в разных частях кода, оно компилируется только один раз. Кэш экономит производительность и совершенно незаметен для пользователя, поэтому такое использование можно считать легитимным. + + +Резюме .[#toc-summary] +---------------------- + +Мы показали, почему это имеет смысл + +1) Убрать из кода все статические переменные +2) Объявить зависимости +3) И использовать инъекцию зависимостей + +Обдумывая дизайн кода, помните, что каждый `static $foo` представляет собой проблему. Для того чтобы ваш код стал средой, уважающей DI, необходимо полностью искоренить глобальное состояние и заменить его инъекцией зависимостей. + +Во время этого процесса вы можете обнаружить, что вам нужно разделить класс, потому что он несет более одной ответственности. Не беспокойтесь об этом; стремитесь к принципу одной ответственности. + +*Я хотел бы поблагодарить Мишко Хевери, чьи статьи, такие как [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/], легли в основу этой главы*. diff --git a/dependency-injection/sl/@home.texy b/dependency-injection/sl/@home.texy index 3246122b8f..4afc5d3e0c 100644 --- a/dependency-injection/sl/@home.texy +++ b/dependency-injection/sl/@home.texy @@ -5,8 +5,9 @@ Injekcija odvisnosti Vbrizgavanje odvisnosti je načrtovalski vzorec, ki bo temeljito spremenil vaš pogled na kodo in razvoj. Odpira pot v svet čisto zasnovanih in trajnostnih aplikacij. - [Kaj je vrivanje odvisnosti? |introduction] -- [Kaj je zabojnik DI? |container] +- [Globalno stanje in enojni elementi |global-state] - [Posredovanje odvisnosti |passing-dependencies] +- [Kaj je zabojnik DI? |container] Nette DI diff --git a/dependency-injection/sl/@left-menu.texy b/dependency-injection/sl/@left-menu.texy index dd817c041b..e89212de5b 100644 --- a/dependency-injection/sl/@left-menu.texy +++ b/dependency-injection/sl/@left-menu.texy @@ -1,8 +1,9 @@ Injekcija odvisnosti ******************** - [Kaj je DI? |introduction] -- [Kaj je vsebnik DI? |container] +- [Globalno stanje in singletoni |global-state] - [Posredovanje odvisnosti |passing-dependencies] +- [Kaj je vsebnik DI? |container] Nette DI diff --git a/dependency-injection/sl/global-state.texy b/dependency-injection/sl/global-state.texy new file mode 100644 index 0000000000..48de9a56ae --- /dev/null +++ b/dependency-injection/sl/global-state.texy @@ -0,0 +1,312 @@ +Globalno stanje in singletoni +***************************** + +.[perex] +Opozorilo: naslednji konstrukti so simptomi slabe zasnove kode: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` ali `static::$var` + +Ali se v vaši kodi pojavlja katera od teh konstrukcij? Potem imate priložnost za izboljšave. Morda mislite, da so to pogosti konstrukti, ki jih vidimo v vzorčnih rešitvah različnih knjižnic in ogrodij. +Na žalost so še vedno jasen pokazatelj slabe zasnove. Skupno jim je eno: uporaba globalnega stanja. + +Zdaj zagotovo ne govorimo o neki akademski čistosti. Uporaba globalnega stanja in singletonov ima uničujoče učinke na kakovost kode. Njeno obnašanje postane nepredvidljivo, zmanjšuje produktivnost razvijalcev in sili vmesnike razredov, da lažejo o svojih resničnih odvisnostih. To zmede programerje. + +V tem poglavju bomo pokazali, kako je to mogoče. + + +Globalno medsebojno povezovanje .[#toc-global-interlinking] +----------------------------------------------------------- + +Temeljna težava globalne države je, da je globalno dostopna. To omogoča pisanje v podatkovno zbirko prek globalne (statične) metode `DB::insert()`. +V idealnem svetu bi moral biti objekt sposoben komunicirati le z drugimi objekti, ki so mu bili [neposredno posredovani |passing-dependencies]. +Če ustvarim dva objekta `A` in `B` in nikoli ne prenesem reference z `A` na `B`, potem niti `A`, niti `B` ne moreta dostopati do drugega objekta ali spreminjati njegovega stanja. +To je zelo zaželena lastnost kode. To je podobno, kot če bi imeli baterijo in žarnico; žarnica ne bo svetila, dokler ju ne povežete z žico. + +To ne velja za globalne (statične) spremenljivke ali singletone. Objekt `A` bi lahko *brezžično* dostopal do objekta `C` in ga spreminjal brez posredovanja reference, tako da bi poklical `C::changeSomething()`. +Če objekt `B` zagrabi tudi globalno spremenljivko `C`, potem lahko `A` in `B` medsebojno komunicirata prek `C`. + +Uporaba globalnih spremenljivk v sistem uvede novo obliko *brezžične* povezave, ki navzven ni vidna. +Ustvarja dimno zaveso, ki otežuje razumevanje in uporabo kode. +Razvijalci morajo prebrati vsako vrstico izvorne kode, da resnično razumejo odvisnosti. Namesto da bi se le seznanili z vmesnikom razredov. +Poleg tega gre za popolnoma nepotrebno povezovanje. + +.[note] +Kar zadeva obnašanje, ni razlike med globalno in statično spremenljivko. So enako škodljive. + + +Strašljivo delovanje na daljavo .[#toc-the-spooky-action-at-a-distance] +----------------------------------------------------------------------- + +"Spooky action at a distance" - tako je Albert Einstein leta 1935 poimenoval pojav v kvantni fiziki, ki ga je spravil ob živce. +Gre za kvantno prepletenost, katere posebnost je, da ko izmerite informacijo o enem delcu, takoj vplivate na drug delec, tudi če sta med seboj oddaljena na milijone svetlobnih let. +kar navidezno krši temeljni zakon vesolja, da nič ne more potovati hitreje od svetlobe. + +V svetu programske opreme lahko "strašljivo delovanje na daljavo" imenujemo situacijo, ko zaženemo proces, za katerega mislimo, da je izoliran (ker mu nismo posredovali nobenih referenc), vendar se na oddaljenih lokacijah sistema zgodijo nepričakovane interakcije in spremembe stanja, o katerih objektu nismo povedali. To se lahko zgodi le prek globalnega stanja. + +Predstavljajte si, da se pridružite skupini za razvoj projekta, ki ima veliko in zrelo bazo kode. Vaš novi vodja vas prosi, da izvedete novo funkcijo, in kot dober razvijalec začnete s pisanjem testa. Ker pa ste novinec v projektu, naredite veliko raziskovalnih testov tipa "kaj se zgodi, če pokličem to metodo". In poskušate napisati naslednji test: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // številko vaše kartice. + $cc->charge(100); +} +``` + +Po določenem času na svojem telefonu opazite obvestila iz banke, da je bilo ob vsakem zagonu na vašo kreditno kartico 🤦‍♂️ zaračunanih 100 dolarjev. + +Kako bi lahko test povzročil dejansko obremenitev? S kreditno kartico ni enostavno upravljati. Sodelovati morate s spletno storitvijo tretje osebe, poznati morate naslov URL te spletne storitve, prijaviti se morate in tako naprej. +Nobena od teh informacij ni vključena v test. Še huje, ne veste niti, kje so te informacije prisotne, in zato ne veste, kako zasmehovati zunanje odvisnosti, da se ob vsakem zagonu ne bi ponovno zaračunalo 100 USD. In kako naj bi kot nov razvijalec vedeli, da bo to, kar boste naredili, privedlo do tega, da boste za 100 dolarjev revnejši? + +To je strašljivo delovanje na daljavo! + +Ne preostane vam drugega, kot da se prekopate skozi veliko izvorne kode in pri tem sprašujete starejše in izkušenejše kolege, dokler ne razumete, kako delujejo povezave v projektu. +To je posledica dejstva, da ob pogledu na vmesnik razreda `CreditCard` ne morete določiti globalnega stanja, ki ga je treba inicializirati. Tudi pogled v izvorno kodo razreda vam ne bo povedal, katero metodo za inicializacijo je treba poklicati. V najboljšem primeru lahko poiščete globalno spremenljivko, do katere se dostopa, in na podlagi tega poskušate uganiti, kako jo inicializirati. + +Razredi v takem projektu so patološki lažnivci. Plačilna kartica se pretvarja, da jo lahko preprosto instancirate in pokličete metodo `charge()`. Vendar na skrivaj sodeluje z drugim razredom, `PaymentGateway`. Tudi njegov vmesnik pravi, da ga je mogoče inicializirati samostojno, v resnici pa iz neke konfiguracijske datoteke potegne poverilnice in tako naprej. +Razvijalcem, ki so napisali to kodo, je jasno, da `CreditCard` potrebuje `PaymentGateway`. Zato so kodo napisali na ta način. Toda za vsakogar, ki je novinec v projektu, je to popolna uganka in ovira učenje. + +Kako popraviti situacijo? Enostavno. **Pustite, da API razglasi odvisnosti.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Opazite, kako so odnosi v kodi nenadoma očitni. Z izjavo, da metoda `charge()` potrebuje `PaymentGateway`, vam ni treba nikogar spraševati, kako je koda medsebojno odvisna. Veste, da morate ustvariti njen primerek, in ko to poskušate storiti, naletite na dejstvo, da morate zagotoviti parametre dostopa. Brez njih se koda sploh ne bi mogla zagnati. + +In kar je najpomembneje, zdaj lahko zasmehujete plačilni prehod, tako da vam ne bo treba plačati 100 dolarjev vsakič, ko boste zagnali test. + +Globalno stanje povzroča, da lahko vaši objekti skrivaj dostopajo do stvari, ki niso deklarirane v njihovih API-jih, in posledično naredi vaše API-je patološke lažnivce. + +Morda o tem še niste razmišljali na ta način, toda kadarkoli uporabljate globalno stanje, ustvarjate skrivne brezžične komunikacijske kanale. Strašljivo delovanje na daljavo sili razvijalce, da preberejo vsako vrstico kode, da bi razumeli morebitne interakcije, zmanjšuje produktivnost razvijalcev in zmede nove člane ekipe. +Če ste kodo ustvarili vi, poznate prave odvisnosti, vsi, ki pridejo za vami, pa so nevedni. + +Ne pišite kode, ki uporablja globalno stanje, temveč raje prenašajte odvisnosti. To je vbrizgavanje odvisnosti. + + +Krhkost globalne države .[#toc-brittleness-of-the-global-state] +--------------------------------------------------------------- + +V kodi, ki uporablja globalno stanje in singletone, nikoli ni gotovo, kdaj in kdo je to stanje spremenil. To tveganje je prisotno že pri inicializaciji. Naslednja koda naj bi ustvarila povezavo s podatkovno bazo in inicializirala plačilni prehod, vendar vedno znova vrže izjemo, iskanje vzroka pa je izredno zamudno: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Podrobno morate pregledati kodo, da ugotovite, da objekt `PaymentGateway` brezžično dostopa do drugih objektov, od katerih nekateri zahtevajo povezavo s podatkovno bazo. Tako morate inicializirati podatkovno zbirko, preden `PaymentGateway`. Vendar vam to skriva dimna zavesa globalnega stanja. Koliko časa bi prihranili, če API vsakega razreda ne bi lagal in deklariral svojih odvisnosti? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Podobna težava se pojavi pri uporabi globalnega dostopa do povezave s podatkovno bazo: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +Pri klicu metode `save()` ni gotovo, ali je bila povezava s podatkovno bazo že ustvarjena in kdo je odgovoren za njeno ustvarjanje. Če bi na primer želeli spremeniti povezavo s podatkovno bazo sproti, morda za namene testiranja, bi verjetno morali ustvariti dodatne metode, kot sta `DB::reconnect(...)` ali `DB::reconnectForTest()`. + +Oglejmo si primer: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Kje se lahko prepričamo, da se testna podatkovna zbirka res uporablja, ko kličemo `$article->save()`? Kaj pa, če je metoda `Foo::doSomething()` spremenila globalno povezavo s podatkovno bazo? Da bi to ugotovili, bi morali pregledati izvorno kodo razreda `Foo` in verjetno še mnogih drugih razredov. Vendar bi takšen pristop zagotovil le kratkoročni odgovor, saj se lahko stanje v prihodnosti spremeni. + +Kaj pa, če povezavo s podatkovno bazo prenesemo v statično spremenljivko znotraj razreda `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +To ne spremeni ničesar. Problem je globalno stanje in ni pomembno, v katerem razredu se skriva. V tem primeru, tako kot v prejšnjem, nimamo pojma, v katero zbirko podatkov se zapiše, ko se kliče metoda `$article->save()`. Kdorkoli na oddaljenem koncu aplikacije lahko kadarkoli spremeni podatkovno zbirko z uporabo metode `Article::setDb()`. Pod našimi rokami. + +Zaradi globalnega stanja je naša aplikacija **izjemno občutljiva**. + +Vendar obstaja preprost način za reševanje te težave. Preprosto zahtevajte, da API razglasi odvisnosti, da se zagotovi pravilno delovanje. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Ta pristop odpravlja skrb zaradi skritih in nepričakovanih sprememb povezav s podatkovno bazo. Zdaj smo prepričani, kje je shranjen članek, in nobena sprememba kode znotraj drugega nepovezanega razreda ne more več spremeniti stanja. Koda ni več krhka, temveč stabilna. + +Ne pišite kode, ki uporablja globalno stanje, temveč raje prenašajte odvisnosti. Tako je na voljo vbrizgavanje odvisnosti (dependency injection). + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton je oblikovni vzorec, ki po [definiciji |https://en.wikipedia.org/wiki/Singleton_pattern] iz znane publikacije Gang of Four omejuje razred na en primerek in mu omogoča globalni dostop. Izvedba tega vzorca je običajno podobna naslednji kodi: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // in druge metode, ki izvajajo funkcije razreda +} +``` + +Na žalost singleton v aplikacijo vnese globalno stanje. Kot smo pokazali zgoraj, je globalno stanje nezaželeno. Zato singleton velja za protivzorec. + +V svoji kodi ne uporabljajte singletonov in jih nadomestite z drugimi mehanizmi. Singletonov resnično ne potrebujete. Če pa morate zagotoviti obstoj enega primerka razreda za celotno aplikacijo, to prepustite [vsebniku DI |container]. +Tako ustvarite aplikacijski singleton ali storitev. S tem razred ne bo več zagotavljal svoje edinstvenosti (tj. ne bo imel metode `getInstance()` in statične spremenljivke) in bo izvajal le svoje funkcije. Tako bo prenehal kršiti načelo ene odgovornosti. + + +Globalno stanje v primerjavi s testi .[#toc-global-state-versus-tests] +---------------------------------------------------------------------- + +Pri pisanju testov predpostavljamo, da je vsak test izolirana enota in da vanj ne vstopa zunanje stanje. In nobeno stanje ne zapusti testov. Ko se test konča, mora zbiralnik smeti samodejno odstraniti vsako stanje, povezano s testom. S tem so testi izolirani. Zato lahko teste izvajamo v poljubnem vrstnem redu. + +Če pa so prisotna globalna stanja/singletoni, se vse te lepe predpostavke porušijo. Stanje lahko vstopi v test in izstopi iz njega. Nenadoma je vrstni red testov lahko pomemben. + +Da bi razvijalci sploh lahko testirali singletone, morajo pogosto omiliti njihove lastnosti, morda tako, da dovolijo zamenjavo primerka z drugim. Takšne rešitve so v najboljšem primeru kretnje, ki ustvarjajo kodo, ki jo je težko vzdrževati in razumeti. Vsak test ali metoda `tearDown()`, ki vpliva na katero koli globalno stanje, mora te spremembe razveljaviti. + +Globalno stanje je največji glavobol pri testiranju enot! + +Kako popraviti situacijo? Enostavno. Ne pišite kode, ki uporablja singletone, ampak raje prenašajte odvisnosti. To je vbrizgavanje odvisnosti. + + +Globalne konstante .[#toc-global-constants] +------------------------------------------- + +Globalno stanje ni omejeno na uporabo singletonov in statičnih spremenljivk, temveč se lahko uporablja tudi za globalne konstante. + +Konstante, katerih vrednost nam ne zagotavlja nobenih novih (`M_PI`) ali koristnih (`PREG_BACKTRACK_LIMIT_ERROR`) informacij, so nedvomno v redu. +Nasprotno pa konstante, ki služijo kot način za *brezžično* posredovanje informacij znotraj kode, niso nič drugega kot skrita odvisnost. Kot je `LOG_FILE` v naslednjem primeru. +Uporaba konstante `FILE_APPEND` je popolnoma pravilna. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +V tem primeru moramo parameter deklarirati v konstruktorju razreda `Foo`, da postane del API-ja: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Zdaj lahko posredujemo informacije o poti do datoteke za beleženje in jih po potrebi preprosto spremenimo, kar olajša testiranje in vzdrževanje kode. + + +Globalne funkcije in statične metode .[#toc-global-functions-and-static-methods] +-------------------------------------------------------------------------------- + +Poudariti želimo, da uporaba statičnih metod in globalnih funkcij sama po sebi ni problematična. Razložili smo neprimernost uporabe `DB::insert()` in podobnih metod, vedno pa je šlo za globalno stanje, shranjeno v statični spremenljivki. Metoda `DB::insert()` zahteva obstoj statične spremenljivke, ker shranjuje povezavo s podatkovno bazo. Brez te spremenljivke metode ne bi bilo mogoče izvesti. + +Uporaba determinističnih statičnih metod in funkcij, kot so `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` in številne druge, je popolnoma skladna z vbrizgavanjem odvisnosti. Te funkcije iz istih vhodnih parametrov vedno vrnejo enake rezultate in so zato predvidljive. Ne uporabljajo nobenega globalnega stanja. + +Vendar v PHP obstajajo funkcije, ki niso deterministične. Med njimi je na primer funkcija `htmlspecialchars()`. Njen tretji parameter, `$encoding`, če ni določen, je privzeta vrednost konfiguracijske možnosti `ini_get('default_charset')`. Zato je priporočljivo, da ta parameter vedno navedete, da se izognete morebitnemu nepredvidljivemu obnašanju funkcije. Nette to dosledno počne. + +Nekatere funkcije, kot so `strtolower()`, `strtoupper()` in podobne, so imele v bližnji preteklosti nedeterministično obnašanje in so bile odvisne od nastavitve `setlocale()`. To je povzročilo številne zaplete, najpogosteje pri delu s turškim jezikom. +Turški jezik namreč razlikuje med velikimi in malimi črkami `I` s piko in brez nje. Tako je `strtolower('I')` vrnil znak `ı`, `strtoupper('i')` pa znak `İ`, zaradi česar so aplikacije povzročale številne skrivnostne napake. +Vendar je bila ta težava odpravljena v različici PHP 8.2 in funkcije niso več odvisne od lokalnega jezika. + +To je lep primer, kako je globalno stanje prizadelo na tisoče razvijalcev po vsem svetu. Rešitev je bila zamenjava z vbrizgavanjem odvisnosti. + + +Kdaj je mogoče uporabiti globalno stanje? .[#toc-when-is-it-possible-to-use-global-state] +----------------------------------------------------------------------------------------- + +V nekaterih posebnih primerih je mogoče uporabiti globalno stanje. Na primer pri razhroščevanju kode, ko morate izpisati vrednost spremenljivke ali izmeriti trajanje določenega dela programa. V takih primerih, ki zadevajo začasna dejanja, ki bodo pozneje odstranjena iz kode, je upravičena uporaba globalno razpoložljivega odlagalnika ali štoparice. Ta orodja niso del zasnove kode. + +Drug primer so funkcije za delo z regularnimi izrazi `preg_*`, ki interno shranjujejo sestavljene regularne izraze v statični predpomnilnik v pomnilniku. Kadar isti regularni izraz večkrat pokličete v različnih delih kode, se sestavi samo enkrat. Predpomnilnik prihrani zmogljivost, poleg tega pa je za uporabnika popolnoma neviden, zato lahko takšno uporabo štejemo za zakonito. + + +Povzetek .[#toc-summary] +------------------------ + +Pokazali smo, zakaj je smiselno + +1) Odstranite vse statične spremenljivke iz kode +2) Deklarirajte odvisnosti +3) In uporabite vbrizgavanje odvisnosti + +Ko razmišljate o oblikovanju kode, imejte v mislih, da vsaka stran `static $foo` predstavlja težavo. Če želite, da bo vaša koda okolje, ki spoštuje DI, nujno popolnoma izkoreniniti globalno stanje in ga nadomestiti z vbrizgavanjem odvisnosti. + +Med tem postopkom boste morda ugotovili, da morate razred razdeliti, ker ima več kot eno odgovornost. Ne skrbite zaradi tega; prizadevajte si za načelo ene odgovornosti. + +*Zahvaljujem se Mišku Heveryju, čigar članki, kot je [Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/], so podlaga za to poglavje.* diff --git a/dependency-injection/tr/@home.texy b/dependency-injection/tr/@home.texy index 7840585b16..df54e0668e 100644 --- a/dependency-injection/tr/@home.texy +++ b/dependency-injection/tr/@home.texy @@ -5,8 +5,9 @@ Bağımlılık Enjeksiyonu Dependency Injection, koda ve geliştirmeye bakış açınızı temelden değiştirecek bir tasarım modelidir. Temiz tasarlanmış ve sürdürülebilir uygulamalardan oluşan bir dünyaya giden yolu açar. - [Dependency Injection Nedir? |introduction] -- [DI Container Nedir? |container] +- [Küresel Durum ve Tekiller |global-state] - [Bağımlılıkları |passing-dependencies]Geçirmek +- [DI Container Nedir? |container] Nette DI diff --git a/dependency-injection/tr/@left-menu.texy b/dependency-injection/tr/@left-menu.texy index 31323b0841..7b8fefad55 100644 --- a/dependency-injection/tr/@left-menu.texy +++ b/dependency-injection/tr/@left-menu.texy @@ -1,8 +1,9 @@ Bağımlılık Enjeksiyonu ********************** - [DI Nedir? |introduction] -- [DI Konteyner Nedir? |container] +- [Küresel Durum ve Tekiller |global-state] - [Bağımlılıkları Geçme |passing-dependencies] +- [DI Konteyner Nedir? |container] Nette DI diff --git a/dependency-injection/tr/global-state.texy b/dependency-injection/tr/global-state.texy new file mode 100644 index 0000000000..d3034f1477 --- /dev/null +++ b/dependency-injection/tr/global-state.texy @@ -0,0 +1,312 @@ +Küresel Durum ve Singletonlar +***************************** + +.[perex] +Uyarı: Aşağıdaki yapılar kötü kod tasarımının belirtileridir: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` veya `static::$var` + +Kodunuzda bu yapılardan herhangi biri var mı? O halde kendinizi geliştirmek için bir fırsatınız var. Bunların çeşitli kütüphanelerin ve çerçevelerin örnek çözümlerinde gördüğümüz yaygın yapılar olduğunu düşünüyor olabilirsiniz. +Ne yazık ki, bunlar hala kötü tasarımın açık bir göstergesidir. Ortak bir noktaları var: küresel durum kullanımı. + +Şimdi, kesinlikle bir tür akademik saflıktan bahsetmiyoruz. Global state ve singleton kullanımının kod kalitesi üzerinde yıkıcı etkileri vardır. Davranışları öngörülemez hale gelir, geliştirici verimliliğini azaltır ve sınıf arayüzlerini gerçek bağımlılıkları hakkında yalan söylemeye zorlar. Bu da programcıların kafasını karıştırır. + +Bu bölümde, bunun nasıl mümkün olduğunu göstereceğiz. + + +Küresel Bağlantı .[#toc-global-interlinking] +-------------------------------------------- + +Global durumla ilgili temel sorun, global olarak erişilebilir olmasıdır. Bu, `DB::insert()` global (statik) yöntemi aracılığıyla veritabanına yazmayı mümkün kılar. +İdeal bir dünyada, bir nesne yalnızca kendisine [doğrudan aktarılan |passing-dependencies] diğer nesnelerle iletişim kurabilmelidir. +Eğer `A` ve `B` adında iki nesne yaratırsam ve `A` 'den `B`'a asla bir referans aktarmazsam, o zaman ne `A` ne de `B` diğer nesneye erişemez veya durumunu değiştiremez. +Bu, kodun çok arzu edilen bir özelliğidir. Bu, bir pil ve bir ampule sahip olmaya benzer; siz onları birbirine bağlayana kadar ampul yanmayacaktır. + +Bu durum global (statik) değişkenler veya tekil değişkenler için geçerli değildir. `A` nesnesi `C` nesnesine *kablosuz* olarak erişebilir ve `C::changeSomething()` adresini çağırarak herhangi bir referans iletmeden onu değiştirebilir. +Eğer `B` nesnesi global `C` nesnesini de yakalarsa, `A` ve `B` birbirleriyle `C` üzerinden etkileşime girebilir. + +Global değişkenlerin kullanımı, sisteme dışarıdan görünmeyen yeni bir *kablosuz* bağlantı biçimi getirir. +Kodun anlaşılmasını ve kullanılmasını zorlaştıran bir sis perdesi yaratır. +Geliştiriciler bağımlılıkları gerçekten anlamak için kaynak kodun her satırını okumalıdır. Sadece sınıfların arayüzüne aşina olmak yerine. +Dahası, bu tamamen gereksiz bir bağlantıdır. + +.[note] +Davranış açısından, global ve statik değişken arasında bir fark yoktur. İkisi de eşit derecede zararlıdır. + + +Uzaktaki Ürkütücü Eylem .[#toc-the-spooky-action-at-a-distance] +--------------------------------------------------------------- + +"Uzaktaki ürkütücü eylem" - Albert Einstein 1935 yılında kuantum fiziğinde kendisini ürküten bir olguyu böyle adlandırmıştı. +Bu kuantum dolanıklığıdır ve özelliği, bir parçacık hakkındaki bilgiyi ölçtüğünüzde, milyonlarca ışık yılı uzakta olsalar bile başka bir parçacığı hemen etkilemenizdir. +Bu da evrenin temel yasası olan hiçbir şeyin ışıktan hızlı gidemeyeceği ilkesini ihlal eder. + +Yazılım dünyasında, izole olduğunu düşündüğümüz bir süreci çalıştırdığımız (çünkü ona herhangi bir referans iletmedik), ancak sistemin nesneye söylemediğimiz uzak konumlarında beklenmedik etkileşimlerin ve durum değişikliklerinin meydana geldiği bir durumu "uzaktan ürkütücü eylem" olarak adlandırabiliriz. Bu yalnızca global durum aracılığıyla gerçekleşebilir. + +Büyük ve olgun bir kod tabanına sahip bir proje geliştirme ekibine katıldığınızı düşünün. Yeni lideriniz sizden yeni bir özelliği hayata geçirmenizi istiyor ve siz de iyi bir geliştirici gibi işe bir test yazarak başlıyorsunuz. Ancak projede yeni olduğunuz için, "bu yöntemi çağırırsam ne olur" türünde çok sayıda keşif testi yaparsınız. Ve aşağıdaki testi yazmaya çalışıyorsunuz: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // kart numaranız + $cc->charge(100); +} +``` + +Kodu belki birkaç kez çalıştırıyorsunuz ve bir süre sonra telefonunuza bankadan gelen bildirimlerde kodu her çalıştırdığınızda kredi kartınızdan 100 $ çekildiğini fark ediyorsunuz 🤦‍♂️ + +Test nasıl olur da gerçek bir ücretlendirmeye neden olabilir? Kredi kartı ile işlem yapmak kolay değildir. Üçüncü taraf bir web hizmetiyle etkileşime girmeniz, bu web hizmetinin URL'sini bilmeniz, oturum açmanız vb. gerekir. +Bu bilgilerin hiçbiri testte yer almıyor. Daha da kötüsü, bu bilgilerin nerede bulunduğunu ve bu nedenle her çalıştırmanın tekrar 100 $ ücretlendirilmesiyle sonuçlanmaması için harici bağımlılıkları nasıl taklit edeceğinizi bile bilmiyorsunuz. Ve yeni bir geliştirici olarak, yapmak üzere olduğunuz şeyin 100 dolar daha fakir olmanıza yol açacağını nereden bilebilirdiniz? + +Bu uzaktan ürkütücü bir hareket! + +Projedeki bağlantıların nasıl çalıştığını anlayana kadar, daha yaşlı ve daha deneyimli meslektaşlarınıza sorarak çok sayıda kaynak kodu incelemekten başka seçeneğiniz yoktur. +Bunun nedeni, `CreditCard` sınıfının arayüzüne baktığınızda, başlatılması gereken global durumu belirleyememenizdir. Sınıfın kaynak koduna bakmak bile size hangi ilklendirme yöntemini çağırmanız gerektiğini söylemeyecektir. En iyi ihtimalle, erişilen global değişkeni bulabilir ve buradan nasıl başlatılacağını tahmin etmeye çalışabilirsiniz. + +Böyle bir projedeki sınıflar patolojik yalancılardır. Ödeme kartı, sadece onu örnekleyebileceğinizi ve `charge()` yöntemini çağırabileceğinizi iddia eder. Ancak, gizlice başka bir sınıf olan `PaymentGateway` ile etkileşim halindedir. Arayüzü bile bağımsız olarak başlatılabileceğini söylüyor, ancak gerçekte bazı yapılandırma dosyalarından kimlik bilgilerini çekiyor vb. +Bu kodu yazan geliştiriciler için `CreditCard` 'un `PaymentGateway`'a ihtiyacı olduğu açıktır. Kodu bu şekilde yazmışlardır. Ancak projede yeni olan herkes için bu tam bir gizemdir ve öğrenmeyi engeller. + +Bu durum nasıl düzeltilir? Çok kolay. **API'nin bağımlılıkları bildirmesine izin verin.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Kod içindeki ilişkilerin birdenbire nasıl belirgin hale geldiğine dikkat edin. `charge()` yönteminin `PaymentGateway` adresine ihtiyaç duyduğunu beyan ederek, kodun nasıl birbirine bağlı olduğunu kimseye sormak zorunda kalmazsınız. Bunun bir örneğini oluşturmanız gerektiğini biliyorsunuz ve bunu yapmaya çalıştığınızda erişim parametreleri sağlamanız gerektiği gerçeğiyle karşılaşıyorsunuz. Onlar olmadan kod çalışmaz bile. + +Ve en önemlisi, artık ödeme ağ geçidini taklit edebilirsiniz, böylece her test çalıştırdığınızda 100 $ ücretlendirilmezsiniz. + +Küresel durum, nesnelerinizin API'lerinde bildirilmeyen şeylere gizlice erişebilmesine neden olur ve sonuç olarak API'lerinizi patolojik yalancılar haline getirir. + +Daha önce bu şekilde düşünmemiş olabilirsiniz, ancak global state kullandığınızda gizli kablosuz iletişim kanalları oluşturmuş olursunuz. Ürkütücü uzaktan eylem, geliştiricileri potansiyel etkileşimleri anlamak için her kod satırını okumaya zorlar, geliştirici verimliliğini azaltır ve yeni ekip üyelerinin kafasını karıştırır. +Kodu oluşturan kişi sizseniz, gerçek bağımlılıkları bilirsiniz, ancak sizden sonra gelenlerin hiçbir şeyden haberi olmaz. + +Global durum kullanan kod yazmayın, bağımlılıkları aktarmayı tercih edin. Yani, bağımlılık enjeksiyonu. + + +Küresel Devletin Kırılganlığı .[#toc-brittleness-of-the-global-state] +--------------------------------------------------------------------- + +Global state ve singleton kullanan kodlarda, bu state'in ne zaman ve kim tarafından değiştirildiği asla kesin değildir. Bu risk başlatma sırasında zaten mevcuttur. Aşağıdaki kodun bir veritabanı bağlantısı oluşturması ve ödeme ağ geçidini başlatması gerekiyor, ancak sürekli bir istisna atıyor ve nedenini bulmak son derece sıkıcı: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + + `PaymentGateway` nesnesinin diğer nesnelere kablosuz olarak eriştiğini ve bunlardan bazılarının veritabanı bağlantısı gerektirdiğini bulmak için kodu ayrıntılı olarak incelemeniz gerekir. Bu nedenle, `PaymentGateway` adresinden önce veritabanını başlatmanız gerekir. Ancak, global durum sis perdesi bunu sizden gizler. Her sınıfın API'si yalan söylemeseydi ve bağımlılıklarını beyan etseydi ne kadar zaman kazanırdınız? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Bir veritabanı bağlantısına genel erişim kullanıldığında da benzer bir sorun ortaya çıkar: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + + `save()` yöntemi çağrıldığında, bir veritabanı bağlantısının zaten oluşturulup oluşturulmadığı ve oluşturulmasından kimin sorumlu olduğu kesin değildir. Örneğin, veritabanı bağlantısını anında değiştirmek istersek, belki de test amacıyla, muhtemelen `DB::reconnect(...)` veya `DB::reconnectForTest()` gibi ek yöntemler oluşturmamız gerekecektir. + +Bir örnek düşünün: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + + `$article->save()` adresini çağırırken test veritabanının gerçekten kullanıldığından nasıl emin olabiliriz? Ya `Foo::doSomething()` yöntemi global veritabanı bağlantısını değiştirdiyse? Bunu öğrenmek için `Foo` sınıfının ve muhtemelen diğer birçok sınıfın kaynak kodunu incelememiz gerekir. Ancak, durum gelecekte değişebileceğinden, bu yaklaşım yalnızca kısa vadeli bir yanıt sağlayacaktır. + +Veritabanı bağlantısını `Article` sınıfının içindeki statik bir değişkene taşırsak ne olur? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Bu hiçbir şeyi değiştirmez. Sorun global bir durumdur ve hangi sınıfta saklandığı önemli değildir. Bu durumda, bir öncekinde olduğu gibi, `$article->save()` yöntemi çağrıldığında hangi veritabanına yazıldığına dair hiçbir ipucumuz yoktur. Uygulamanın uzak ucundaki herhangi biri `Article::setDb()` adresini kullanarak istediği zaman veritabanını değiştirebilir. Elimizin altında. + +Küresel durum uygulamamızı **son derece kırılgan** hale getirir. + +Ancak bu sorunun üstesinden gelmenin basit bir yolu vardır. Uygun işlevselliği sağlamak için API'nin bağımlılıkları bildirmesi yeterlidir. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Bu yaklaşım, veritabanı bağlantılarında gizli ve beklenmedik değişiklikler yapılması endişesini ortadan kaldırır. Artık makalenin nerede saklandığından eminiz ve başka bir ilgisiz sınıfın içindeki hiçbir kod değişikliği artık durumu değiştiremez. Kod artık kırılgan değil, kararlı. + +Global durum kullanan kod yazmayın, bağımlılıkları aktarmayı tercih edin. Böylece, bağımlılık enjeksiyonu. + + +Singleton .[#toc-singleton] +--------------------------- + +Singleton, ünlü Gang of Four yayınındaki [tanımıyla |https://en.wikipedia.org/wiki/Singleton_pattern], bir sınıfı tek bir örnekle sınırlayan ve ona global erişim sunan bir tasarım modelidir. Bu kalıbın uygulaması genellikle aşağıdaki koda benzer: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // ve sınıfın işlevlerini yerine getiren diğer yöntemler +} +``` + +Ne yazık ki, singleton uygulamaya global durum ekler. Ve yukarıda gösterdiğimiz gibi, global durum istenmeyen bir durumdur. Bu yüzden singleton bir antipattern olarak kabul edilir. + +Kodunuzda singleton kullanmayın ve bunları başka mekanizmalarla değiştirin. Tekillere gerçekten ihtiyacınız yok. Ancak, tüm uygulama için bir sınıfın tek bir örneğinin varlığını garanti etmeniz gerekiyorsa, bunu [DI konteynerine |container] bırakın. +Böylece, bir uygulama singletonu veya hizmeti oluşturun. Bu, sınıfın kendi benzersizliğini sağlamasını durduracak (yani, bir `getInstance()` yöntemine ve statik bir değişkene sahip olmayacaktır) ve yalnızca işlevlerini yerine getirecektir. Böylece, tek sorumluluk ilkesini ihlal etmeyi durduracaktır. + + +Testlere Karşı Küresel Durum .[#toc-global-state-versus-tests] +-------------------------------------------------------------- + +Testleri yazarken, her testin izole bir birim olduğunu ve hiçbir dış durumun teste girmediğini varsayarız. Ve hiçbir durum testleri terk etmez. Bir test tamamlandığında, testle ilişkili tüm durumlar çöp toplayıcı tarafından otomatik olarak kaldırılmalıdır. Bu, testleri yalıtılmış hale getirir. Bu nedenle testleri istediğimiz sırada çalıştırabiliriz. + +Ancak, küresel durumlar/singletonlar mevcutsa, tüm bu güzel varsayımlar bozulur. Bir durum bir teste girebilir ve çıkabilir. Birdenbire, testlerin sırası önemli olabilir. + +Tekil öğeleri test etmek için, geliştiricilerin genellikle bir örneğin başka bir örnekle değiştirilmesine izin vererek özelliklerini gevşetmeleri gerekir. Bu tür çözümler, en iyi ihtimalle, bakımı ve anlaşılması zor kodlar üreten hilelerdir. Herhangi bir global durumu etkileyen herhangi bir test veya yöntem `tearDown()` bu değişiklikleri geri almalıdır. + +Global durum, birim testindeki en büyük baş ağrısıdır! + +Bu durum nasıl düzeltilir? Çok kolay. Singleton kullanan kod yazmayın, bağımlılıkları aktarmayı tercih edin. Yani bağımlılık enjeksiyonu. + + +Küresel Sabitler .[#toc-global-constants] +----------------------------------------- + +Küresel durum tekil ve statik değişkenlerin kullanımıyla sınırlı değildir, aynı zamanda küresel sabitler için de geçerli olabilir. + +Değeri bize yeni (`M_PI`) veya faydalı (`PREG_BACKTRACK_LIMIT_ERROR`) bilgi sağlamayan sabitler açıkça tamamdır. +Tersine, kod içinde *kablosuz* bilgi aktarmanın bir yolu olarak hizmet eden sabitler, gizli bir bağımlılıktan başka bir şey değildir. Aşağıdaki örnekte `LOG_FILE` gibi. + `FILE_APPEND` sabitini kullanmak tamamen doğrudur. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Bu durumda, API'nin bir parçası haline getirmek için parametreyi `Foo` sınıfının kurucusunda bildirmeliyiz: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Artık günlük dosyasının yolu hakkında bilgi aktarabilir ve gerektiğinde kolayca değiştirebiliriz, bu da kodu test etmeyi ve bakımını yapmayı kolaylaştırır. + + +Global İşlevler ve Statik Yöntemler .[#toc-global-functions-and-static-methods] +------------------------------------------------------------------------------- + +Statik yöntemlerin ve global fonksiyonların kullanımının kendi içinde sorunlu olmadığını vurgulamak istiyoruz. `DB::insert()` ve benzeri yöntemlerin kullanılmasının uygunsuzluğunu açıkladık, ancak bu her zaman statik bir değişkende saklanan global durum meselesi olmuştur. `DB::insert()` yöntemi, veritabanı bağlantısını sakladığı için statik bir değişkenin varlığını gerektirir. Bu değişken olmadan yöntemi uygulamak imkansızdır. + + `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` ve diğerleri gibi deterministik statik yöntem ve fonksiyonların kullanımı bağımlılık enjeksiyonu ile tamamen tutarlıdır. Bu fonksiyonlar her zaman aynı girdi parametrelerinden aynı sonuçları döndürür ve bu nedenle öngörülebilirdir. Herhangi bir global durum kullanmazlar. + +Ancak, PHP'de deterministik olmayan işlevler de vardır. Bunlara örnek olarak `htmlspecialchars()` fonksiyonu verilebilir. Üçüncü parametresi olan `$encoding` belirtilmezse, varsayılan olarak `ini_get('default_charset')` yapılandırma seçeneğinin değerini alır. Bu nedenle, fonksiyonun olası öngörülemeyen davranışını önlemek için bu parametrenin her zaman belirtilmesi önerilir. Nette bunu sürekli olarak yapar. + + `strtolower()`, `strtoupper()` ve benzerleri gibi bazı fonksiyonlar yakın geçmişte deterministik olmayan davranışlara sahipti ve `setlocale()` ayarına bağlıydı. Bu, çoğunlukla Türkçe diliyle çalışırken birçok komplikasyona neden olmuştur. +Bunun nedeni, Türkçe dilinin noktalı ve noktasız büyük ve küçük harf `I` arasında ayrım yapmasıdır. Bu yüzden `strtolower('I')`, `ı` karakterini ve `strtoupper('i')`, `İ` karakterini döndürüyordu, bu da uygulamaların bir dizi gizemli hataya neden olmasına yol açıyordu. +Ancak, bu sorun PHP 8.2 sürümünde düzeltilmiştir ve işlevler artık yerel ayara bağlı değildir. + +Bu, küresel durumun dünyanın dört bir yanındaki binlerce geliştiriciyi nasıl rahatsız ettiğinin güzel bir örneğidir. Çözüm, bağımlılık enjeksiyonu ile değiştirmekti. + + +Global State Ne Zaman Kullanılabilir? .[#toc-when-is-it-possible-to-use-global-state] +------------------------------------------------------------------------------------- + +Global durumu kullanmanın mümkün olduğu bazı özel durumlar vardır. Örneğin, kodda hata ayıklama yaparken bir değişkenin değerini dökmeniz veya programın belirli bir bölümünün süresini ölçmeniz gerekir. Daha sonra koddan kaldırılacak geçici eylemlerle ilgili olan bu gibi durumlarda, global olarak kullanılabilen bir dumper veya kronometre kullanmak meşrudur. Bu araçlar kod tasarımının bir parçası değildir. + +Başka bir örnek, derlenmiş düzenli ifadeleri dahili olarak bellekteki statik bir önbellekte saklayan `preg_*` düzenli ifadelerle çalışma işlevleridir. Aynı düzenli ifadeyi kodun farklı bölümlerinde birden çok kez çağırdığınızda, bu ifade yalnızca bir kez derlenir. Önbellek performans tasarrufu sağlar ve ayrıca kullanıcı için tamamen görünmezdir, bu nedenle bu tür kullanım meşru kabul edilebilir. + + +Özet .[#toc-summary] +-------------------- + +Bunun neden mantıklı olduğunu gösterdik + +1) Koddan tüm statik değişkenleri kaldırın +2) Bağımlılıkları beyan edin +3) Ve bağımlılık enjeksiyonu kullanın + +Kod tasarımını düşünürken, her `static $foo` adresinin bir sorunu temsil ettiğini unutmayın. Kodunuzun DI'ya saygılı bir ortam olması için, global durumu tamamen ortadan kaldırmak ve bağımlılık enjeksiyonu ile değiştirmek çok önemlidir. + +Bu süreç sırasında, birden fazla sorumluluğu olduğu için bir sınıfı bölmeniz gerektiğini fark edebilirsiniz. Bu konuda endişelenmeyin; tek sorumluluk ilkesi için çaba gösterin. + +*[Flaw: Brittle Global State & Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/] gibi makaleleri bu bölümün temelini oluşturan Miško Hevery'ye teşekkür ederim.* diff --git a/dependency-injection/uk/@home.texy b/dependency-injection/uk/@home.texy index 16aed36c8a..736766758b 100644 --- a/dependency-injection/uk/@home.texy +++ b/dependency-injection/uk/@home.texy @@ -5,8 +5,9 @@ Dependency Injection - це патерн проектування, який докорінно змінить ваш погляд на код і розробку. Він відкриває шлях до світу чисто спроектованих і стійких додатків. - [Що таке Dependency Injection? |introduction] -- [Що таке DI-контейнер? |container] +- [Глобальний стан та синглетони |global-state] - [Передача залежностей |passing-dependencies] +- [Що таке DI-контейнер? |container] Nette DI diff --git a/dependency-injection/uk/@left-menu.texy b/dependency-injection/uk/@left-menu.texy index 2d5abd2a6a..235299080b 100644 --- a/dependency-injection/uk/@left-menu.texy +++ b/dependency-injection/uk/@left-menu.texy @@ -1,8 +1,9 @@ Впровадження залежностей ************************ - [Що таке "впровадження залежностей"? |introduction] -- [Що таке "DI-контейнер"? |container] +- [Глобальний стан та синглетони |global-state] - [Передача залежностей |passing-dependencies] +- [Що таке "DI-контейнер"? |container] Nette DI diff --git a/dependency-injection/uk/global-state.texy b/dependency-injection/uk/global-state.texy new file mode 100644 index 0000000000..85242211cd --- /dev/null +++ b/dependency-injection/uk/global-state.texy @@ -0,0 +1,312 @@ +Глобальна держава та одинаки +**************************** + +.[perex] +Попередження: наступні конструкції є симптомами поганого дизайну коду: + +- `Foo::getInstance()` +- `DB::insert(...)` +- `Article::setDb($db)` +- `ClassName::$var` або `static::$var` + +У вашому коді зустрічається будь-яка з цих конструкцій? Тоді у вас є можливість покращити його. Можливо, ви думаєте, що це звичайні конструкції, які ми бачимо у зразках рішень різних бібліотек та фреймворків. +На жаль, вони все ще є явним індикатором поганого дизайну. Їх об'єднує одне: використання глобального стану. + +Звісно, ми не говоримо про якусь академічну чистоту. Використання глобального стану та синглетонів руйнівно впливає на якість коду. Його поведінка стає непередбачуваною, знижує продуктивність розробника і змушує інтерфейси класів брехати про свої справжні залежності. Що збиває з пантелику програмістів. + +У цій главі ми покажемо, як це можливо. + + +Глобальне взаємозв'язування .[#toc-global-interlinking] +------------------------------------------------------- + +Фундаментальною проблемою глобального стану є те, що він є глобально доступним. Це робить можливим запис до бази даних через глобальний (статичний) метод `DB::insert()`. +В ідеальному світі об'єкт повинен мати можливість взаємодіяти тільки з тими об'єктами, які були йому [безпосередньо передані |passing-dependencies]. +Якщо я створюю два об'єкти `A` і `B` і ніколи не передаю посилання з `A` на `B`, то ні `A`, ні `B` не зможуть отримати доступ до іншого об'єкта або змінити його стан. +Це дуже бажана властивість коду. Це схоже на наявність батарейки і лампочки; лампочка не засвітиться, поки ви не з'єднаєте їх разом. + +Це не стосується глобальних (статичних) змінних або синглетонів. Об'єкт `A` може отримати *бездротовий* доступ до об'єкта `C` і модифікувати його, не передаючи жодних посилань, викликавши `C::changeSomething()`. +Якщо об'єкт `B` також захоплює глобальний `C`, то `A` і `B` можуть взаємодіяти один з одним через `C`. + +Використання глобальних змінних вводить в систему нову форму *бездротового* зв'язку, яку не видно ззовні. +Це створює димову завісу, що ускладнює розуміння і використання коду. +Розробники повинні читати кожен рядок вихідного коду, щоб по-справжньому зрозуміти залежності. Замість того, щоб просто ознайомитися з інтерфейсом класів. +До того ж, це абсолютно непотрібна зв'язка. + +.[note] +З точки зору поведінки, немає ніякої різниці між глобальною і статичною змінною. Вони однаково шкідливі. + + +Страшна дія на відстані .[#toc-the-spooky-action-at-a-distance] +--------------------------------------------------------------- + +"Моторошна дія на відстані" - так Альберт Ейнштейн назвав явище у квантовій фізиці, від якого у 1935 році у нього мурашки по шкірі. +Це квантова заплутаність, особливість якої полягає в тому, що коли ви вимірюєте інформацію про одну частинку, ви одразу ж впливаєте на іншу частинку, навіть якщо вони знаходяться на відстані мільйонів світлових років одна від одної. +Що, здавалося б, порушує фундаментальний закон Всесвіту, згідно з яким ніщо не може рухатися швидше за світло. + +У світі програмного забезпечення ми можемо назвати "моторошною дією на відстані" ситуацію, коли ми запускаємо процес, який вважаємо ізольованим (бо не передали йому жодних посилань), але у віддалених місцях системи відбуваються неочікувані взаємодії та зміни стану, про які ми не повідомили об'єкту. Це може відбуватися тільки через глобальний стан. + +Уявіть, що ви приєдналися до команди розробників проекту, яка має велику, зрілу кодову базу. Ваш новий керівник просить вас реалізувати нову функцію і, як хороший розробник, ви починаєте з написання тесту. Але оскільки ви новачок у проекті, ви робите багато дослідницьких тестів типу "що станеться, якщо я викличу цей метод". І ви намагаєтеся написати наступний тест: + +```php +function testCreditCardCharge() +{ + $cc = new CreditCard('1234567890123456', 5, 2028); // номер вашої картки + $cc->charge(100); +} +``` + +Ви запускаєте код, можливо, кілька разів, і через деякий час помічаєте на своєму телефоні повідомлення від банку про те, що кожного разу, коли ви його запускали, з вашої кредитної картки було знято $100 🤦‍♂️. + +Як тест міг спричинити реальне списання коштів? Працювати з кредитною карткою непросто. Ви повинні взаємодіяти зі стороннім веб-сервісом, знати URL-адресу цього веб-сервісу, увійти в систему і так далі. +Жодна з цих відомостей не міститься в тесті. Навіть гірше, ви навіть не знаєте, де ця інформація присутня, а отже, як імітувати зовнішні залежності так, щоб кожен запуск не призводив до повторного списання 100 доларів. І як розробник-початківець, звідки ви могли знати, що те, що ви збираєтесь робити, призведе до того, що ви станете на 100 доларів біднішими? + +На відстані це виглядає моторошно! + +У вас немає іншого вибору, окрім як перелопатити купу вихідного коду, розпитуючи старших і досвідченіших колег, поки ви не зрозумієте, як працюють зв'язки в проекті. +Це пов'язано з тим, що, дивлячись на інтерфейс класу `CreditCard`, ви не можете визначити глобальний стан, який потрібно ініціалізувати. Навіть перегляд вихідного коду класу не підкаже вам, який метод ініціалізації викликати. У кращому випадку, ви можете знайти глобальну змінну, до якої здійснюється доступ, і спробувати здогадатися, як її ініціалізувати, виходячи з цього. + +Класи в такому проекті - патологічні брехуни. Платіжна картка робить вигляд, що ви можете просто створити її екземпляр і викликати метод `charge()`. Однак вона таємно взаємодіє з іншим класом, `PaymentGateway`. Навіть в її інтерфейсі написано, що вона може ініціалізуватися самостійно, але насправді вона витягує облікові дані з якогось конфігураційного файлу і так далі. +Розробникам, які написали цей код, зрозуміло, що `CreditCard` потрібен `PaymentGateway`. Вони написали код саме так. Але для новачків у проекті це повна загадка і заважає навчанню. + +Як виправити ситуацію? Дуже просто. **Дозвольте API оголошувати залежності.** + +```php +function testCreditCardCharge() +{ + $gateway = new PaymentGateway(/* ... */); + $cc = new CreditCard('1234567890123456', 5, 2028); + $cc->charge($gateway, 100); +} +``` + +Зверніть увагу, як взаємозв'язки в коді стають раптово очевидними. Оголосивши, що метод `charge()` потребує `PaymentGateway`, вам не потрібно нікого питати, як код взаємозалежний. Ви знаєте, що маєте створити його екземпляр, і коли ви намагаєтесь це зробити, то стикаєтесь з тим, що вам потрібно вказати параметри доступу. Без них код навіть не запуститься. + +І найголовніше, тепер ви можете погратися з платіжним шлюзом, щоб з вас не знімали $100 щоразу, коли ви запускаєте тест. + +Глобальний стан призводить до того, що ваші об'єкти можуть таємно отримувати доступ до речей, які не оголошені в їхніх API, і, як наслідок, робить ваші API патологічними брехунами. + +Можливо, ви не замислювалися про це раніше, але кожного разу, коли ви використовуєте глобальний стан, ви створюєте таємні бездротові канали зв'язку. Моторошні віддалені дії змушують розробників читати кожен рядок коду, щоб зрозуміти потенційні взаємодії, знижують продуктивність розробників і заплутують нових членів команди. +Якщо ви той, хто створив код, ви знаєте реальні залежності, але будь-хто, хто прийде після вас, нічого не знає. + +Не пишіть код, який використовує глобальний стан, краще передавайте залежності. Тобто, ін'єкція залежностей. + + +Крихкість глобальної держави .[#toc-brittleness-of-the-global-state] +-------------------------------------------------------------------- + +У коді, який використовує глобальний стан та синглетони, ніколи не можна бути впевненим, коли і ким цей стан було змінено. Цей ризик присутній вже на етапі ініціалізації. Наступний код повинен створити з'єднання з базою даних та ініціалізувати платіжний шлюз, але він постійно видає виключення, і пошук причини цього є надзвичайно нудним: + +```php +PaymentGateway::init(); +DB::init('mysql:', 'user', 'password'); +``` + +Ви повинні детально переглянути код, щоб виявити, що об'єкт `PaymentGateway` отримує доступ до інших об'єктів бездротовим способом, деякі з яких вимагають з'єднання з базою даних. Таким чином, ви повинні ініціалізувати базу даних перед `PaymentGateway`. Однак димова завіса глобального стану приховує це від вас. Скільки часу ви б заощадили, якби API кожного класу не брехав і оголошував свої залежності? + +```php +$db = new DB('mysql:', 'user', 'password'); +$gateway = new PaymentGateway($db, ...); +``` + +Схожа проблема виникає при використанні глобального доступу до з'єднання з базою даних: + +```php +use Illuminate\Support\Facades\DB; + +class Article +{ + public function save(): void + { + DB::insert(/* ... */); + } +} +``` + +При виклику методу `save()` немає впевненості, що з'єднання з базою даних вже створено і хто відповідає за його створення. Наприклад, якщо ми захочемо змінити підключення до бази даних на льоту, можливо, з метою тестування, нам, ймовірно, доведеться створити додаткові методи, такі як `DB::reconnect(...)` або `DB::reconnectForTest()`. + +Розглянемо приклад: + +```php +$article = new Article; +// ... +DB::reconnectForTest(); +Foo::doSomething(); +$article->save(); +``` + +Де ми можемо бути впевнені, що при виклику `$article->save()` дійсно використовується тестова база даних? Що, якщо метод `Foo::doSomething()` змінив глобальне з'єднання з базою даних? Щоб з'ясувати це, нам доведеться вивчити вихідний код класу `Foo` і, ймовірно, багатьох інших класів. Однак такий підхід дасть лише короткострокову відповідь, оскільки в майбутньому ситуація може змінитися. + +Що, якщо ми перемістимо підключення до бази даних у статичну змінну всередині класу `Article`? + +```php +class Article +{ + private static DB $db; + + public static function setDb(DB $db): void + { + self::$db = $db; + } + + public function save(): void + { + self::$db->insert(/* ... */); + } +} +``` + +Це нічого не змінить. Проблема в глобальному стані, і не має значення, в якому класі він ховається. У цьому випадку, як і в попередньому, ми не маємо жодного уявлення про те, до якої бази даних відбувається запис, коли викликається метод `$article->save()`. Будь-хто на віддаленому кінці програми може в будь-який момент змінити базу даних за допомогою методу `Article::setDb()`. Під нашими руками. + +Глобальний стан робить наш додаток **надзвичайно вразливим**. + +Однак є простий спосіб вирішити цю проблему. Просто змусьте API декларувати залежності, щоб забезпечити належну функціональність. + +```php +class Article +{ + public function __construct( + private DB $db, + ) { + } + + public function save(): void + { + $this->db->insert(/* ... */); + } +} + +$article = new Article($db); +// ... +Foo::doSomething(); +$article->save(); +``` + +Такий підхід позбавляє вас від необхідності турбуватися про приховані та неочікувані зміни у з'єднаннях з базою даних. Тепер ми точно знаємо, де зберігається стаття, і жодні модифікації коду в іншому, не пов'язаному з нею класі, більше не зможуть змінити ситуацію. Код більше не крихкий, а стабільний. + +Не пишіть код, який використовує глобальний стан, краще передавайте залежності. Таким чином, ін'єкція залежностей. + + +Синглтон .[#toc-singleton] +-------------------------- + +Синглтон - це патерн проектування, який, за [визначенням |https://en.wikipedia.org/wiki/Singleton_pattern] з відомої публікації Gang of Four, обмежує клас єдиним екземпляром і пропонує глобальний доступ до нього. Реалізація цього патерну зазвичай нагадує наступний код: + +```php +class Singleton +{ + private static self $instance; + + public static function getInstance(): self + { + self::$instance ??= new self; + return self::$instance; + } + + // та інші методи, що виконують функції класу +} +``` + +На жаль, синглтон вводить в додаток глобальний стан. А як ми показали вище, глобальний стан небажаний. Саме тому синглтон вважається антипаттерном. + +Не використовуйте синглетони у своєму коді і замініть їх іншими механізмами. Синглетони насправді не потрібні. Однак, якщо вам потрібно гарантувати існування єдиного екземпляру класу для всього додатку, залиште це [контейнеру DI |container]. +Таким чином, створіть синглтон додатку, або сервіс. Тоді клас не буде надавати власної унікальності (тобто не матиме методу `getInstance()` та статичної змінної) і виконуватиме лише свої функції. Таким чином, він перестане порушувати принцип єдиної відповідальності. + + +Глобальний стан проти тестів .[#toc-global-state-versus-tests] +-------------------------------------------------------------- + +При написанні тестів ми припускаємо, що кожен тест є ізольованою одиницею, і жоден зовнішній стан не входить до нього. І жоден стан не виходить з тесту. Коли тест завершується, будь-який стан, пов'язаний з тестом, повинен бути автоматично видалений збирачем сміття. Це робить тести ізольованими. Тому ми можемо запускати тести у будь-якому порядку. + +Однак, якщо присутні глобальні стани/синглетони, всі ці гарні припущення руйнуються. Стан може входити і виходити з тесту. Несподівано, порядок виконання тестів може мати значення. + +Щоб взагалі тестувати синглетони, розробникам часто доводиться послаблювати їх властивості, можливо, дозволяючи замінювати один екземпляр іншим. Такі рішення, в кращому випадку, є хаками, які створюють код, який важко підтримувати і розуміти. Будь-який тест або метод `tearDown()`, який впливає на будь-який глобальний стан, повинен скасувати ці зміни. + +Глобальний стан - це найбільший головний біль у модульному тестуванні! + +Як виправити ситуацію? Дуже просто. Не пишіть код, який використовує синглетони, краще передавайте залежності. Тобто, ін'єкція залежностей. + + +Глобальні константи .[#toc-global-constants] +-------------------------------------------- + +Глобальний стан не обмежується використанням синглетонів і статичних змінних, але також може застосовуватися до глобальних констант. + +З константами, значення яких не надає нам ніякої нової (`M_PI`) або корисної (`PREG_BACKTRACK_LIMIT_ERROR`) інформації, явно все гаразд. +І навпаки, константи, які слугують способом "бездротової" передачі інформації всередині коду, є нічим іншим, як прихованою залежністю. Як `LOG_FILE` у наступному прикладі. +Використання константи `FILE_APPEND` є абсолютно коректним. + +```php +const LOG_FILE = '...'; + +class Foo +{ + public function doSomething() + { + // ... + file_put_contents(LOG_FILE, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +У цьому випадку ми повинні оголосити параметр у конструкторі класу `Foo`, щоб зробити його частиною API: + +```php +class Foo +{ + public function __construct( + private string $logFile, + ) { + } + + public function doSomething() + { + // ... + file_put_contents($this->logFile, $message . "\n", FILE_APPEND); + // ... + } +} +``` + +Тепер ми можемо передавати інформацію про шлях до файлу логування і легко змінювати його за потреби, що полегшує тестування і підтримку коду. + + +Глобальні функції та статичні методи .[#toc-global-functions-and-static-methods] +-------------------------------------------------------------------------------- + +Ми хочемо підкреслити, що використання статичних методів і глобальних функцій саме по собі не є проблематичним. Ми вже пояснювали недоречність використання `DB::insert()` та подібних методів, але завжди йшлося про глобальний стан, що зберігається у статичній змінній. Метод `DB::insert()` вимагає наявності статичної змінної, оскільки він зберігає з'єднання з базою даних. Без цієї змінної реалізація методу була б неможливою. + +Використання детермінованих статичних методів і функцій, таких як `DateTime::createFromFormat()`, `Closure::fromCallable`, `strlen()` і багатьох інших, цілком узгоджується з ін'єкцією залежностей. Ці функції завжди повертають однакові результати від однакових вхідних параметрів і тому є передбачуваними. Вони не використовують жодного глобального стану. + +Однак, в PHP є функції, які не є детермінованими. До них відноситься, наприклад, функція `htmlspecialchars()`. Її третій параметр, `$encoding`, якщо не вказано, за замовчуванням приймає значення параметра конфігурації `ini_get('default_charset')`. Тому рекомендується завжди вказувати цей параметр, щоб уникнути можливої непередбачуваної поведінки функції. Nette послідовно це робить. + +Деякі функції, такі як `strtolower()`, `strtoupper()`, тощо, у недалекому минулому мали недетерміновану поведінку і залежали від параметра `setlocale()`. Це викликало багато ускладнень, найчастіше при роботі з турецькою мовою. +Це пов'язано з тим, що турецька мова розрізняє верхній і нижній регістр `I` з крапкою і без неї. Тому `strtolower('I')` повертав символ `ı`, а `strtoupper('i')` повертав символ `İ`, що призводило до того, що додатки викликали ряд загадкових помилок. +Однак ця проблема була виправлена у версії PHP 8.2, і функції більше не залежать від локалі. + +Це гарний приклад того, як глобальний стан дошкуляв тисячам розробників по всьому світу. Рішенням було замінити його на ін'єкцію залежностей. + + +Коли можна використовувати глобальну державу? .[#toc-when-is-it-possible-to-use-global-state] +--------------------------------------------------------------------------------------------- + +Існують певні специфічні ситуації, коли можна використовувати глобальний стан. Наприклад, під час налагодження коду, коли потрібно вивести значення змінної або виміряти тривалість виконання певної частини програми. У таких випадках, коли йдеться про тимчасові дії, які згодом будуть видалені з коду, правомірно використовувати глобально доступний дампер або секундомір. Ці інструменти не є частиною дизайну коду. + +Інший приклад - функції для роботи з регулярними виразами `preg_*`, які внутрішньо зберігають скомпільовані регулярні вирази в статичному кеші в пам'яті. Коли ви викликаєте один і той самий регулярний вираз кілька разів у різних частинах коду, він компілюється лише один раз. Кеш економить продуктивність, а також є повністю невидимим для користувача, тому таке використання можна вважати легітимним. + + +Підсумок .[#toc-summary] +------------------------ + +Ми показали, чому це має сенс + +1) Видалити всі статичні змінні з коду +2) Оголосити залежності +3) І використовувати ін'єкцію залежностей + +Коли ви обмірковуєте дизайн коду, пам'ятайте, що кожне `static $foo` представляє проблему. Для того, щоб ваш код був середовищем, що поважає DI, важливо повністю викорінити глобальний стан і замінити його на ін'єкцію залежностей. + +Під час цього процесу ви можете виявити, що вам потрібно розділити клас, оскільки він має більше однієї відповідальності. Не турбуйтеся про це; прагніть до принципу єдиної відповідальності. + +*Я хотів би подякувати Мішко Хевері, чиї статті, такі як [Flaw: Brittle Global State та Singletons |http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/], лягли в основу цієї глави. Я хотів би подякувати Мішко Хевері.* From 33a5e798a8970ed10dcefed91b3ac23977a1f1eb Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 14 Mar 2023 00:08:57 +0100 Subject: [PATCH 02/47] typo --- contributing/cs/code.texy | 2 +- dependency-injection/cs/passing-dependencies.texy | 4 ++-- tester/cs/testing-with-travis.texy | 4 +--- tester/en/testing-with-travis.texy | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/contributing/cs/code.texy b/contributing/cs/code.texy index c5cb874e87..4730f3179d 100644 --- a/contributing/cs/code.texy +++ b/contributing/cs/code.texy @@ -74,7 +74,7 @@ Posílání Pull requestu Pokud jste se změnami spokojeni, odešlete je na GitHub. ```shell -git push původu new_branch_name +git push origin new_branch_name ``` Kód už je veřejně dostupný, ale musíte je odeslat do hlavní větve (masteru) v repozitáři Nette. Udělejte tzv. [pull request |https://help.github.com/articles/creating-a-pull-request]. diff --git a/dependency-injection/cs/passing-dependencies.texy b/dependency-injection/cs/passing-dependencies.texy index 719f45baa1..2ef1fb6b59 100644 --- a/dependency-injection/cs/passing-dependencies.texy +++ b/dependency-injection/cs/passing-dependencies.texy @@ -87,7 +87,7 @@ $service->setCache($cache); Tento způsob je vhodný pro nepovinné závislosti, které nejsou pro funkci třídy nezbytné, neboť není garantováno, že objekt závislost skutečně dostane (tj. že uživatel metodu zavolá). -Zároveň tento způsob připouští volat setter opakovaně a závislost tak měnit. Pokud to není žádoucí, přidáme do metody kontrolu, nebo od PHP 8.1 označíme proměnnou `$cache` příznakem `readonly`. +Zároveň tento způsob připouští volat setter opakovaně a závislost tak měnit. Pokud to není žádoucí, přidáme do metody kontrolu, nebo od PHP 8.1 označíme property `$cache` příznakem `readonly`. ```php class MyService @@ -104,7 +104,7 @@ class MyService } ``` -Volání setteru definujeme v konfiraci DI kontejneru v [sekci setup |services#Setup]. I tady se využívá automatického předávání závislostí pomocí autowiringu: +Volání setteru definujeme v konfiguraci DI kontejneru v [klíči setup |services#Setup]. I tady se využívá automatického předávání závislostí pomocí autowiringu: ```neon services: diff --git a/tester/cs/testing-with-travis.texy b/tester/cs/testing-with-travis.texy index 350a535a38..ef7855f014 100644 --- a/tester/cs/testing-with-travis.texy +++ b/tester/cs/testing-with-travis.texy @@ -108,9 +108,7 @@ matrix: ``` .[note] -Všimněte si, že jsme uvedli pouze verzi php. Travis se zachová tak, že do celkového výsledku nezahrne všechny kombinace s verzí `nightly`. - -Pokud nyní testy na nightly selžou, ale v jiných prostředích projdou, bude i celý build označen jako procházející. +Všimněte si, že jsme uvedli pouze verzi PHP. Travis se zachová tak, že do celkového výsledku nezahrne všechny kombinace s verzí `nightly`. Pokud nyní testy na nightly selžou, ale v jiných prostředích projdou, bude i celý build označen jako procházející. Spouštění testů diff --git a/tester/en/testing-with-travis.texy b/tester/en/testing-with-travis.texy index c3b74801d5..cca605b0ff 100644 --- a/tester/en/testing-with-travis.texy +++ b/tester/en/testing-with-travis.texy @@ -108,7 +108,7 @@ matrix: ``` .[note] -Notice that when allowing failures, we only specified php version, not environment variables. This means all jobs with nightly version will be allowed to fail, nevermind the environment variables values. It works with `exclude` aswell. +Note that we have only listed the PHP version. Travis behaves in a way that does not include all combinations with the `nightly` version in the total result. Now if tests fail on nightly but pass in other environments, the whole build will be marked as passing as well. Running the Tests From d6d255577f117148a1125283987fa8ffcb09500f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 14 Mar 2023 17:00:30 +0100 Subject: [PATCH 03/47] ```bash -> shell --- best-practices/bg/editors-and-tools.texy | 4 ++-- best-practices/cs/editors-and-tools.texy | 4 ++-- best-practices/de/editors-and-tools.texy | 4 ++-- best-practices/el/editors-and-tools.texy | 4 ++-- best-practices/en/editors-and-tools.texy | 4 ++-- best-practices/es/editors-and-tools.texy | 4 ++-- best-practices/fr/editors-and-tools.texy | 4 ++-- best-practices/hu/editors-and-tools.texy | 4 ++-- best-practices/it/editors-and-tools.texy | 4 ++-- best-practices/pl/editors-and-tools.texy | 4 ++-- best-practices/pt/editors-and-tools.texy | 4 ++-- best-practices/ro/editors-and-tools.texy | 4 ++-- best-practices/ru/editors-and-tools.texy | 4 ++-- best-practices/sl/editors-and-tools.texy | 4 ++-- best-practices/tr/editors-and-tools.texy | 4 ++-- best-practices/uk/editors-and-tools.texy | 4 ++-- forms/bg/validation.texy | 2 +- forms/cs/validation.texy | 2 +- forms/de/validation.texy | 2 +- forms/el/validation.texy | 2 +- forms/en/validation.texy | 2 +- forms/es/validation.texy | 2 +- forms/fr/validation.texy | 2 +- forms/hu/validation.texy | 2 +- forms/it/validation.texy | 2 +- forms/pl/validation.texy | 2 +- forms/pt/validation.texy | 2 +- forms/ro/validation.texy | 2 +- forms/ru/validation.texy | 2 +- forms/sl/validation.texy | 2 +- forms/tr/validation.texy | 2 +- forms/uk/validation.texy | 2 +- 32 files changed, 48 insertions(+), 48 deletions(-) diff --git a/best-practices/bg/editors-and-tools.texy b/best-practices/bg/editors-and-tools.texy index d39f58b782..86ee4a91f8 100644 --- a/best-practices/bg/editors-and-tools.texy +++ b/best-practices/bg/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan е инструмент, който открива логически г Инсталирайте го чрез Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: И след това го оставете да анализира класовете в папката `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/cs/editors-and-tools.texy b/best-practices/cs/editors-and-tools.texy index 6fc1f2a67e..e65c322423 100644 --- a/best-practices/cs/editors-and-tools.texy +++ b/best-practices/cs/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan je nástroj, který odhalí logické chyby v kódu dřív, než jej spus Nainstalujeme jej pomocí Composeru: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: A následně jej necháme zanalyzovat třídy ve složce `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/de/editors-and-tools.texy b/best-practices/de/editors-and-tools.texy index b72808e4b1..46d986e163 100644 --- a/best-practices/de/editors-and-tools.texy +++ b/best-practices/de/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan ist ein Werkzeug, das logische Fehler in Ihrem Code aufspürt, bevor Sie Installieren Sie es über Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Und lassen Sie dann die Klassen im Ordner `app/` analysieren: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/el/editors-and-tools.texy b/best-practices/el/editors-and-tools.texy index 5d29827e06..3e894b27ea 100644 --- a/best-practices/el/editors-and-tools.texy +++ b/best-practices/el/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan .[#toc-phpstan] Εγκαταστήστε το μέσω του Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Και στη συνέχεια αφήστε το να αναλύσει τις κλάσεις στο φάκελο `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/en/editors-and-tools.texy b/best-practices/en/editors-and-tools.texy index 8241088593..b8446fea90 100644 --- a/best-practices/en/editors-and-tools.texy +++ b/best-practices/en/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan is a tool that detects logical errors in your code before you run it. Install it via Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: And then let it analyze the classes in the `app/` folder: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/es/editors-and-tools.texy b/best-practices/es/editors-and-tools.texy index 54ce688d1b..cb037a479b 100644 --- a/best-practices/es/editors-and-tools.texy +++ b/best-practices/es/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan es una herramienta que detecta errores lógicos en su código antes de e Instálelo a través de Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Y luego deja que analice las clases en la carpeta `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/fr/editors-and-tools.texy b/best-practices/fr/editors-and-tools.texy index a04115630b..61513bfaf6 100644 --- a/best-practices/fr/editors-and-tools.texy +++ b/best-practices/fr/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan est un outil qui détecte les erreurs logiques dans votre code avant que Installez-le via Composer : -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Et ensuite laissez-le analyser les classes dans le dossier `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/hu/editors-and-tools.texy b/best-practices/hu/editors-and-tools.texy index 4819d8ea71..9d0a0f1cc6 100644 --- a/best-practices/hu/editors-and-tools.texy +++ b/best-practices/hu/editors-and-tools.texy @@ -30,7 +30,7 @@ A PHPStan egy olyan eszköz, amely még a futtatás előtt felismeri a logikai h Telepítse a Composer segítségével: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Majd hagyja, hogy elemezze a `app/` mappában lévő osztályokat: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/it/editors-and-tools.texy b/best-practices/it/editors-and-tools.texy index 37b0b52500..bdafe93ca9 100644 --- a/best-practices/it/editors-and-tools.texy +++ b/best-practices/it/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan è uno strumento che rileva gli errori logici nel codice prima di esegui Si installa tramite Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: E poi lasciare che analizzi le classi nella cartella `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/pl/editors-and-tools.texy b/best-practices/pl/editors-and-tools.texy index 74c3c6c6fc..fee21ea5bc 100644 --- a/best-practices/pl/editors-and-tools.texy +++ b/best-practices/pl/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan to narzędzie, które wykrywa błędy logiczne w Twoim kodzie, zanim go Instalujemy go za pomocą programu Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: A potem niech analizuje klasy w folderze `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/pt/editors-and-tools.texy b/best-practices/pt/editors-and-tools.texy index 3e256beeaa..8eb691a59e 100644 --- a/best-practices/pt/editors-and-tools.texy +++ b/best-practices/pt/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan é uma ferramenta que detecta erros lógicos em seu código antes de exe Instale-o através do Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: E depois deixe que ele analise as classes na pasta `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/ro/editors-and-tools.texy b/best-practices/ro/editors-and-tools.texy index 136ed55b3f..c16ba93df7 100644 --- a/best-practices/ro/editors-and-tools.texy +++ b/best-practices/ro/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan este un instrument care detectează erorile logice din codul dvs. înain Instalați-l prin Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Și apoi lăsați-l să analizeze clasele din dosarul `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/ru/editors-and-tools.texy b/best-practices/ru/editors-and-tools.texy index e9021eae7b..fcb05471e7 100644 --- a/best-practices/ru/editors-and-tools.texy +++ b/best-practices/ru/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan — это инструмент, который обнаруживает Установите его через Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: А затем позвольте ему проанализировать классы в папке `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/sl/editors-and-tools.texy b/best-practices/sl/editors-and-tools.texy index 8f1c46b036..b0cb974942 100644 --- a/best-practices/sl/editors-and-tools.texy +++ b/best-practices/sl/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan je orodje, ki odkriva logične napake v vaši kodi, preden jo zaženete. Namestite ga prek programa Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Nato naj analizira razrede v mapi `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/tr/editors-and-tools.texy b/best-practices/tr/editors-and-tools.texy index 233a7bf3ab..6c40de4d40 100644 --- a/best-practices/tr/editors-and-tools.texy +++ b/best-practices/tr/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan, siz çalıştırmadan önce kodunuzdaki mantıksal hataları tespit ede Composer aracılığıyla yükleyin: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: Ve sonra `app/` klasöründeki sınıfları analiz etmesine izin verin: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/best-practices/uk/editors-and-tools.texy b/best-practices/uk/editors-and-tools.texy index b7e51b9620..66372f474e 100644 --- a/best-practices/uk/editors-and-tools.texy +++ b/best-practices/uk/editors-and-tools.texy @@ -30,7 +30,7 @@ PHPStan - це інструмент, який виявляє логічні по Встановіть його через Composer: -```bash +```shell composer require --dev phpstan/phpstan-nette ``` @@ -49,7 +49,7 @@ parameters: А потім дозвольте йому проаналізувати класи в папці `app/`: -```bash +```shell vendor/bin/phpstan analyse app ``` diff --git a/forms/bg/validation.texy b/forms/bg/validation.texy index 28901e139a..3caa7d15f9 100644 --- a/forms/bg/validation.texy +++ b/forms/bg/validation.texy @@ -292,7 +292,7 @@ $form->addText('zip', 'postcode:') Или инсталирайте чрез [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/cs/validation.texy b/forms/cs/validation.texy index 819d6445ac..a2232fe491 100644 --- a/forms/cs/validation.texy +++ b/forms/cs/validation.texy @@ -292,7 +292,7 @@ Nebo zkopírovat lokálně do veřejné složky projektu (např. z `vendor/nette Nebo nainstalovat přes [npm|https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/de/validation.texy b/forms/de/validation.texy index 8335b37961..3b35a02ae8 100644 --- a/forms/de/validation.texy +++ b/forms/de/validation.texy @@ -292,7 +292,7 @@ Oder kopieren Sie es lokal in den öffentlichen Ordner des Projekts (z. B. von ` Oder über [npm |https://www.npmjs.com/package/nette-forms] installieren: -```bash +```shell npm install nette-forms ``` diff --git a/forms/el/validation.texy b/forms/el/validation.texy index 297dd536ef..f619c8c2c0 100644 --- a/forms/el/validation.texy +++ b/forms/el/validation.texy @@ -292,7 +292,7 @@ $form->addText('zip', 'Postcode:') Ή εγκαταστήστε μέσω [του npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/en/validation.texy b/forms/en/validation.texy index d8c53fdd17..9d144b4c58 100644 --- a/forms/en/validation.texy +++ b/forms/en/validation.texy @@ -292,7 +292,7 @@ Or copy locally to the public folder of the project (e.g. from `vendor/nette/for Or install via [npm|https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/es/validation.texy b/forms/es/validation.texy index a5974e94f9..3ad0b76dd1 100644 --- a/forms/es/validation.texy +++ b/forms/es/validation.texy @@ -292,7 +292,7 @@ O copiarlo localmente en la carpeta pública del proyecto (por ejemplo, desde `v O instalar a través de [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/fr/validation.texy b/forms/fr/validation.texy index fdf9797815..2d2623330d 100644 --- a/forms/fr/validation.texy +++ b/forms/fr/validation.texy @@ -292,7 +292,7 @@ Ou le copier localement dans le dossier public du projet (par exemple à partir Ou installer via [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/hu/validation.texy b/forms/hu/validation.texy index c90d71cf7a..7fd997f488 100644 --- a/forms/hu/validation.texy +++ b/forms/hu/validation.texy @@ -292,7 +292,7 @@ Vagy másolja be helyileg a projekt nyilvános mappájába (pl. a `vendor/nette/ Vagy telepítse az [npm |https://www.npmjs.com/package/nette-forms] segítségével: -```bash +```shell npm install nette-forms ``` diff --git a/forms/it/validation.texy b/forms/it/validation.texy index e9ce00d9f0..7f435fd398 100644 --- a/forms/it/validation.texy +++ b/forms/it/validation.texy @@ -292,7 +292,7 @@ Oppure copiare localmente nella cartella pubblica del progetto (ad esempio da `v Oppure installare tramite [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/pl/validation.texy b/forms/pl/validation.texy index 0ca69803f0..75e7c62d54 100644 --- a/forms/pl/validation.texy +++ b/forms/pl/validation.texy @@ -292,7 +292,7 @@ Albo skopiuj go lokalnie do publicznego folderu projektu (np. z `vendor/nette/fo Lub zainstalować za pośrednictwem [npm: |https://www.npmjs.com/package/nette-forms] -```bash +```shell npm install nette-forms ``` diff --git a/forms/pt/validation.texy b/forms/pt/validation.texy index c2332a4a09..36a257c50a 100644 --- a/forms/pt/validation.texy +++ b/forms/pt/validation.texy @@ -292,7 +292,7 @@ Ou copiar localmente para a pasta pública do projeto (por exemplo, de `vendor/n Ou instalar via [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/ro/validation.texy b/forms/ro/validation.texy index 74c1d1d0b1..3aac33825d 100644 --- a/forms/ro/validation.texy +++ b/forms/ro/validation.texy @@ -292,7 +292,7 @@ Sau copiați local în folderul public al proiectului (de exemplu, de la `vendor Sau instalați prin [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/ru/validation.texy b/forms/ru/validation.texy index dd0efea7f3..df6e71d5da 100644 --- a/forms/ru/validation.texy +++ b/forms/ru/validation.texy @@ -292,7 +292,7 @@ $form->addText('zip', 'Почтовый индекс:') Или установите через [npm|https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/sl/validation.texy b/forms/sl/validation.texy index 2ad03bbfd3..e2b1ce220b 100644 --- a/forms/sl/validation.texy +++ b/forms/sl/validation.texy @@ -292,7 +292,7 @@ Lahko pa jo kopirate lokalno v javno mapo projekta (npr. s spletne strani `vendo ali namestite prek [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` diff --git a/forms/tr/validation.texy b/forms/tr/validation.texy index e98a660897..bb45ad8d60 100644 --- a/forms/tr/validation.texy +++ b/forms/tr/validation.texy @@ -292,7 +292,7 @@ Ya da yerel olarak projenin ortak klasörüne kopyalayın (örneğin `vendor/net Veya [npm |https://www.npmjs.com/package/nette-forms] aracılığıyla yükleyin: -```bash +```shell npm install nette-forms ``` diff --git a/forms/uk/validation.texy b/forms/uk/validation.texy index 7e833e0a68..9d38011c8b 100644 --- a/forms/uk/validation.texy +++ b/forms/uk/validation.texy @@ -292,7 +292,7 @@ $form->addText('zip', 'Поштовий індекс:') Або встановіть через [npm |https://www.npmjs.com/package/nette-forms]: -```bash +```shell npm install nette-forms ``` From 44cd6909f117cd58a472325a9f190c9e47c1093c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 14 Mar 2023 14:00:04 +0100 Subject: [PATCH 04/47] composer: improved content --- best-practices/bg/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/cs/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/de/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/el/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/en/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/es/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/fr/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/hu/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/it/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/pl/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/pt/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/ro/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/ru/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/sl/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/tr/composer.texy | 46 ++++++++++++++++++++++++++++----- best-practices/uk/composer.texy | 46 ++++++++++++++++++++++++++++----- 16 files changed, 624 insertions(+), 112 deletions(-) diff --git a/best-practices/bg/composer.texy b/best-practices/bg/composer.texy index 1d9cd82c26..b8a26ebb20 100644 --- a/best-practices/bg/composer.texy +++ b/best-practices/bg/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Актуализиране до най-новата версия .[#toc-update-to-the-latest-version] -======================================================================= +Актуализиране на пакетите до най-новите версии .[#toc-update-packages-to-the-latest-versions] +============================================================================================= За да обновите всички използвани пакети до най-новата версия в съответствие с ограниченията на версиите, определени в `composer.json`, използвайте командата `composer update`. Например зависимостта `"nette/database": "^3.0"` ще инсталира най-новата версия 3.x.x, но не и версия 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Вместо `name-of-the-project` посочете името на директорията на проекта и изпълнете командата. Composer ще изтегли хранилището `nette/web-project` от GitHub, което вече съдържа файла `composer.json`, и веднага след това ще инсталира самата рамка Nette. Остава само да [проверите разрешенията за запис на |nette:troubleshooting#Setting-Directory-Permissions] директориите `temp/` и `log/`, и сте готови да започнете работа. +Ако знаете на каква версия на PHP ще бъде хостван проектът, не забравяйте да [я настро |#PHP Version]ите. + Версия на PHP .[#toc-php-version] ================================= -Composer винаги инсталира версии на пакети, които са съвместими с версията на PHP, която използвате в момента. Разбира се, това може да не е същата версия на PHP, която е инсталирана на вашия хост. Затова е добре да добавите информация за версията на PHP на хоста към `composer.json`, така че да бъдат инсталирани само съвместимите версии на пакетите: +Composer винаги инсталира версиите на пакетите, които са съвместими с версията на PHP, която използвате в момента (или по-скоро с версията на PHP, използвана в командния ред при стартиране на Composer). Вероятно това не е същата версия, която използва вашият уеб хост. Ето защо е много важно да добавите информация за версията на PHP на вашия хостинг във файла `composer.json`. След това ще бъдат инсталирани само версии на пакети, съвместими с хоста. + +Например, за да настроите проекта да работи с PHP 8.2.3, използвайте командата: + +```shell +composer config platform.php 8.2.3 +``` + +По този начин версията се записва във файла `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP версия сервера + "php": "8.2.3" } } } ``` +Номерът на версията на PHP обаче е посочен и на друго място във файла, в раздела `require`. Докато първият номер определя версията, за която ще бъдат инсталирани пакетите, вторият номер казва за каква версия е написано самото приложение. +(Разбира се, няма смисъл тези версии да се различават, така че двойното вписване е излишно.) Тази версия се задава с командата: + +```shell +composer require php 8.2.3 --no-update +``` + +Или директно във файла `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Фалшиви доклади .[#toc-false-reports] +===================================== + +При надграждане на пакети или промяна на номера на версиите се случват конфликти. Един пакет има изисквания, които са в конфликт с друг и т.н. Понякога обаче Composer отпечатва фалшиви съобщения. Той съобщава за конфликт, който в действителност не съществува. В този случай помага да изтриете файла `composer.lock` и да опитате отново. + +Ако съобщението за грешка продължава, то е мислено сериозно и трябва да прочетете от него какво и как да промените. + Packagist.org - глобално хранилище .[#toc-packagist-org-global-repository] ========================================================================== diff --git a/best-practices/cs/composer.texy b/best-practices/cs/composer.texy index 12ba37407d..2f0c503870 100644 --- a/best-practices/cs/composer.texy +++ b/best-practices/cs/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Aktualizace na nejnovější verze -=============================== +Aktualizace balíčků na nejnovější verze +======================================= Aktualizaci použiváných knihoven na nejnovější verze podle podmínek definovaných v `composer.json` má na starosti příkaz `composer update`. Např. u závislosti `"nette/database": "^3.0"` nainstaluje nejnovější verzi 3.x.x, ale nikoliv už verzi 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project nazev-projektu Jako `nazev-projektu` vložte název adresáře pro svůj projekt a potvrďte. Composer stáhne repozitář `nette/web-project` z GitHubu, který už obsahuje soubor `composer.json`, a hned potom Nette Framework. Mělo by již stačit pouze [nastavit oprávnění |nette:troubleshooting#nastaveni-prav-adresaru] na zápis do složek `temp/` a `log/` a projekt by měl ožít. +Pokud víte, na jaké verzi bude PHP projekt hostován, nezapomeňte [ji nastavit |#Verze PHP]. + Verze PHP ========= -Composer vždy instaluje ty verze balíčků, které jsou kompatibilní s verzí PHP, kterou právě používáte. Což ovšem nemusí být stejná verze, jako používá váš hosting. Proto je užitečné si do souboru `composer.json` přidat informaci o verzi PHP na hostingu a poté se budou instalovat pouze verze balíčků s hostingem kompatibilní: +Composer vždy instaluje ty verze balíčků, které jsou kompatibilní s verzí PHP, kterou právě používáte (lépe řečeno s verzí PHP používanou v příkazové řádce při spouštění Composeru). Což ale nejspíš není stejná verze, jakou používá váš hosting. Proto je velmi důležité si do souboru `composer.json` přidat informaci o verzi PHP na hostingu. Poté se budou instalovat pouze verze balíčků s hostingem kompatibilní. + +To, že projekt poběží například na PHP 8.2.3, nastavíme příkazem: + +```shell +composer config platform.php 8.2.3 +``` + +Takto se verze zapíše do souboru `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # verze PHP na hostingu + "php": "8.2.3" } } } ``` +Nicméně číslo verze PHP se uvádí ještě na jiném místě souboru, a to v sekci `require`. Zatímco první číslo určuje, pro jakou verzi se budou instalovat balíčky, druhé číslo říká, pro jakou verzi je napsaná samotná aplikace. +A podle něj například PhpStorm nastavuje *PHP language level*. (Samozřejmě nedává smysl, aby se tyto verze lišily, takže dvojí zápis je nedomyšlenost.) Tuto verzi nastavíte příkazem: + +```shell +composer require php 8.2.3 --no-update +``` + +Nebo přímo v souboru `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Planá hlášení +============= + +Při upgradu balíčků nebo změnách čísel verzí se stává, že dojde ke konfliktu. Jeden balíček má požadavky, které jsou v rozporu s jiným a podobně. Composer ale občas vypisuje plané hlášení. Hlásí konflikt, který reálně neexistuje. V takovém případě pomůže smazat soubor `composer.lock` a zkusit to znovu. + +Pokud chybová hláška přetrvává, pak je myšlena vážně a je potřeba z ní vyčíst, co a jak upravit. + Packagist.org - centrální repozitář =================================== diff --git a/best-practices/de/composer.texy b/best-practices/de/composer.texy index b7dfaf1032..eaaa86a842 100644 --- a/best-practices/de/composer.texy +++ b/best-practices/de/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Update auf die neueste Version .[#toc-update-to-the-latest-version] -=================================================================== +Pakete auf die neuesten Versionen aktualisieren .[#toc-update-packages-to-the-latest-versions] +============================================================================================== Um alle verwendeten Pakete auf die neueste Version gemäß den in `composer.json` definierten Versionsbeschränkungen zu aktualisieren, verwenden Sie den Befehl `composer update`. Zum Beispiel wird für die Abhängigkeit `"nette/database": "^3.0"` die neueste Version 3.x.x installiert, aber nicht Version 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Anstelle von `name-of-the-project` sollten Sie den Namen des Verzeichnisses für Ihr Projekt angeben und den Befehl ausführen. Composer wird das Repository `nette/web-project` von GitHub holen, das bereits die Datei `composer.json` enthält, und gleich danach das Nette Framework selbst installieren. Nun müssen nur noch die [Schreibrechte |nette:troubleshooting#setting-directory-permissions] für die Verzeichnisse `temp/` und `log/` [überprüft |nette:troubleshooting#setting-directory-permissions] werden und schon kann es losgehen. +Wenn Sie wissen, mit welcher PHP-Version das Projekt gehostet werden soll, sollten Sie [diese |#PHP Version] unbedingt einrichten. + PHP-Version .[#toc-php-version] =============================== -Composer installiert immer die Versionen der Pakete, die mit der PHP-Version kompatibel sind, die Sie gerade verwenden. Das kann natürlich nicht die gleiche Version sein wie die von PHP auf Ihrem Webhost. Daher ist es sinnvoll, der Datei `composer.json` Informationen über die PHP-Version auf dem Host hinzuzufügen, damit nur die mit dem Host kompatiblen Versionen der Pakete installiert werden: +Composer installiert immer die Versionen der Pakete, die mit der PHP-Version kompatibel sind, die Sie gerade verwenden (oder besser gesagt, die PHP-Version, die auf der Kommandozeile verwendet wird, wenn Sie Composer starten). Das ist wahrscheinlich nicht die Version, die Ihr Webhost verwendet. Deshalb ist es sehr wichtig, dass Sie Informationen über die PHP-Version Ihres Hosts in Ihre Datei `composer.json` aufnehmen. Danach werden nur noch die Versionen der Pakete installiert, die mit dem Host kompatibel sind. + +Um zum Beispiel das Projekt auf PHP 8.2.3 einzustellen, verwenden Sie den Befehl: + +```shell +composer config platform.php 8.2.3 +``` + +So wird die Version in die Datei `composer.json` geschrieben: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Die PHP-Versionsnummer wird jedoch auch an anderer Stelle in der Datei aufgeführt, im Abschnitt `require`. Während die erste Zahl die Version angibt, für die Pakete installiert werden, sagt die zweite Zahl, für welche Version die Anwendung selbst geschrieben wurde. +(Natürlich macht es keinen Sinn, dass diese Versionen unterschiedlich sind, daher ist die doppelte Angabe eine Redundanz). Sie setzen diese Version mit dem Befehl: + +```shell +composer require php 8.2.3 --no-update +``` + +Oder direkt in der Datei "Composer.json": + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +False Berichte .[#toc-false-reports] +==================================== + +Bei der Aktualisierung von Paketen oder der Änderung von Versionsnummern kommt es zu Konflikten. Ein Paket hat Anforderungen, die mit einem anderen in Konflikt stehen, und so weiter. Composer gibt jedoch gelegentlich eine falsche Meldung aus. Er meldet einen Konflikt, der nicht wirklich existiert. In diesem Fall hilft es, die Datei `composer.lock` zu löschen und es erneut zu versuchen. + +Bleibt die Fehlermeldung bestehen, dann ist sie ernst gemeint und Sie müssen ihr entnehmen, was Sie wie ändern müssen. + Packagist.org - Globales Repository .[#toc-packagist-org-global-repository] =========================================================================== diff --git a/best-practices/el/composer.texy b/best-practices/el/composer.texy index c8725bbb67..0971df08e3 100644 --- a/best-practices/el/composer.texy +++ b/best-practices/el/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Ενημέρωση στην τελευταία έκδοση .[#toc-update-to-the-latest-version] -==================================================================== +Ενημέρωση πακέτων στις τελευταίες εκδόσεις .[#toc-update-packages-to-the-latest-versions] +========================================================================================= Για να ενημερώσετε όλα τα χρησιμοποιούμενα πακέτα στην τελευταία έκδοση σύμφωνα με τους περιορισμούς έκδοσης που ορίζονται στο `composer.json` χρησιμοποιήστε την εντολή `composer update`. Για παράδειγμα, για την εξάρτηση `"nette/database": "^3.0"` θα εγκαταστήσει την τελευταία έκδοση 3.x.x, αλλά όχι την έκδοση 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Αντί για το `name-of-the-project` θα πρέπει να δώσετε το όνομα του καταλόγου για το έργο σας και να εκτελέσετε την εντολή. Το Composer θα φέρει το αποθετήριο `nette/web-project` από το GitHub, το οποίο περιέχει ήδη το αρχείο `composer.json`, και αμέσως μετά θα εγκαταστήσει το ίδιο το Nette Framework. Το μόνο που απομένει είναι να [ελέγξετε τα δικαιώματα εγγραφής |nette:troubleshooting#setting-directory-permissions] στους καταλόγους `temp/` και `log/` και είστε έτοιμοι να ξεκινήσετε. +Αν γνωρίζετε σε ποια έκδοση PHP θα φιλοξενηθεί το έργο, φροντίστε να [το ρυθμίσετε |#PHP Version]. + Έκδοση PHP .[#toc-php-version] ============================== -Το Composer εγκαθιστά πάντα τις εκδόσεις των πακέτων που είναι συμβατές με την έκδοση της PHP που χρησιμοποιείτε αυτή τη στιγμή. Η οποία, βέβαια, μπορεί να μην είναι η ίδια έκδοση με την PHP του web host σας. Ως εκ τούτου, είναι χρήσιμο να προσθέσετε πληροφορίες σχετικά με την έκδοση της PHP στον κεντρικό υπολογιστή στο αρχείο `composer.json`, και τότε θα εγκατασταθούν μόνο οι εκδόσεις των πακέτων που είναι συμβατές με τον κεντρικό υπολογιστή: +Το Composer εγκαθιστά πάντα τις εκδόσεις των πακέτων που είναι συμβατές με την έκδοση της PHP που χρησιμοποιείτε αυτή τη στιγμή (ή μάλλον, την έκδοση της PHP που χρησιμοποιείται στη γραμμή εντολών όταν εκτελείτε το Composer). Η οποία πιθανότατα δεν είναι η ίδια έκδοση που χρησιμοποιεί ο web host σας. Γι' αυτό είναι πολύ σημαντικό να προσθέσετε πληροφορίες σχετικά με την έκδοση της PHP στη φιλοξενία σας στο αρχείο `composer.json`. Μετά από αυτό, θα εγκατασταθούν μόνο οι εκδόσεις των πακέτων που είναι συμβατές με τον host. + +Για παράδειγμα, για να ρυθμίσετε το έργο να τρέχει σε PHP 8.2.3, χρησιμοποιήστε την εντολή: + +```shell +composer config platform.php 8.2.3 +``` + +Έτσι γράφεται η έκδοση στο αρχείο `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Ωστόσο, ο αριθμός έκδοσης της PHP αναφέρεται και σε άλλο σημείο του αρχείου, στην ενότητα `require`. Ενώ ο πρώτος αριθμός καθορίζει την έκδοση για την οποία θα εγκατασταθούν τα πακέτα, ο δεύτερος αριθμός λέει για ποια έκδοση είναι γραμμένη η ίδια η εφαρμογή. +(Φυσικά, δεν έχει νόημα αυτές οι εκδόσεις να είναι διαφορετικές, οπότε η διπλή καταχώρηση είναι πλεονασμός). Ορίζετε αυτή την έκδοση με την εντολή: + +```shell +composer require php 8.2.3 --no-update +``` + +Ή απευθείας στο αρχείο `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Ψευδείς αναφορές .[#toc-false-reports] +====================================== + +Όταν αναβαθμίζετε πακέτα ή αλλάζετε αριθμούς εκδόσεων, συμβαίνουν συγκρούσεις. Ένα πακέτο έχει απαιτήσεις που συγκρούονται με ένα άλλο και ούτω καθεξής. Ωστόσο, το Composer εκτυπώνει περιστασιακά ψευδή μηνύματα. Αναφέρει μια σύγκρουση που στην πραγματικότητα δεν υπάρχει. Σε αυτή την περίπτωση, βοηθάει να διαγράψετε το αρχείο `composer.lock` και να δοκιμάσετε ξανά. + +Αν το μήνυμα σφάλματος επιμένει, τότε εννοείται σοβαρά και πρέπει να διαβάσετε από αυτό τι πρέπει να τροποποιήσετε και πώς. + Packagist.org - Παγκόσμιο αποθετήριο .[#toc-packagist-org-global-repository] ============================================================================ diff --git a/best-practices/en/composer.texy b/best-practices/en/composer.texy index efee53307c..e87a29b0f7 100644 --- a/best-practices/en/composer.texy +++ b/best-practices/en/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Update to the Latest Version -============================ +Update Packages to the Latest Versions +====================================== To update all used packages to the latest version according to version constraints defined in `composer.json` use command `composer update`. For example for dependency `"nette/database": "^3.0"` it will install the latest version 3.x.x, but not version 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Instead the `name-of-the-project` you should provide the name of the directory for your project and execute the command. Composer will fetch the `nette/web-project` repository from GitHub, which already contains the `composer.json` file, and right after that install the Nette Framework itself. The only thing which remains is to [check write permissions |nette:troubleshooting#setting-directory-permissions] on directories `temp/` and `log/` and you're ready to go. +If you know what version of PHP the project will be hosted on, be sure to [set it up |#PHP Version]. + PHP Version =========== -Composer always installs those versions of packages that are compatible with the version of PHP you are currently using. Which, of course, may not be the same version as PHP on your web host. Therefore, it is useful to add information about the PHP version on the host to the `composer.json` file, and then only versions of packages compatible with host will be installed: +Composer always installs the versions of packages that are compatible with the version of PHP you are currently using (or rather, the version of PHP used on the command line when you run Composer). Which is probably not the same version your web host is using. That's why it's very important to add information about the PHP version on your hosting to your `composer.json` file. After that, only versions of packages compatible with the host will be installed. + +For example, to set the project to run on PHP 8.2.3, use the command: + +```shell +composer config platform.php 8.2.3 +``` + +This is how the version is written to the `composer.json` file: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +However, the PHP version number is also listed elsewhere in the file, in the `require` section. While the first number specifies the version for which packages will be installed, the second number tells what version the application itself is written for. +(Of course, it doesn't make sense for these versions to be different, so double entry is a redundancy.) You set this version with the command: + +```shell +composer require php 8.2.3 --no-update +``` + +Or directly in the `composer.json` file: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +False Reports +============= + +When upgrading packages or changing version numbers, conflicts happen. One package has requirements that conflict with another and so on. However, Composer occasionally prints a false messages. It reports a conflict that doesn't really exist. In this case, it helps to delete the `composer.lock` file and try again. + +If the error message persists, then it is meant seriously and you need to read from it what to modify and how. + Packagist.org - Global Repository ================================= diff --git a/best-practices/es/composer.texy b/best-practices/es/composer.texy index be50786337..c550b6a92f 100644 --- a/best-practices/es/composer.texy +++ b/best-practices/es/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Actualizar a la última versión .[#toc-update-to-the-latest-version] -=================================================================== +Actualizar paquetes a las últimas versiones .[#toc-update-packages-to-the-latest-versions] +========================================================================================== Para actualizar todos los paquetes utilizados a la última versión según las restricciones de versión definidas en `composer.json` utilice el comando `composer update`. Por ejemplo, para la dependencia `"nette/database": "^3.0"` instalará la última versión 3.x.x, pero no la versión 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project En lugar de `name-of-the-project` debe proporcionar el nombre del directorio para su proyecto y ejecutar el comando. Composer obtendrá el repositorio `nette/web-project` de GitHub, que ya contiene el archivo `composer.json`, y justo después instalará el propio Nette Framework. La única cosa que queda es [comprobar los permisos de escritura |nette:troubleshooting#setting-directory-permissions] en los directorios `temp/` y `log/` y ya está listo para ir. +Si sabes en qué versión de PHP se alojará el proyecto, asegúrate de [configurarlo |#PHP Version]. + Versión PHP .[#toc-php-version] =============================== -Composer siempre instala las versiones de los paquetes que son compatibles con la versión de PHP que está utilizando actualmente. Que, por supuesto, puede no ser la misma versión que PHP en su host web. Por lo tanto, es útil añadir información sobre la versión de PHP en el host al archivo `composer.json`, y entonces sólo se instalarán las versiones de paquetes compatibles con el host: +Composer siempre instala las versiones de los paquetes que son compatibles con la versión de PHP que está utilizando actualmente (o más bien, la versión de PHP utilizada en la línea de comandos cuando se ejecuta Composer). Que probablemente no es la misma versión que utiliza su proveedor de alojamiento web. Por eso es muy importante añadir información sobre la versión de PHP de tu alojamiento a tu archivo `composer.json`. Después de eso, sólo se instalarán las versiones de paquetes compatibles con el host. + +Por ejemplo, para configurar el proyecto para que se ejecute en PHP 8.2.3, utilice el comando: + +```shell +composer config platform.php 8.2.3 +``` + +Así se escribe la versión en el archivo `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Sin embargo, el número de versión de PHP también aparece en otra parte del archivo, en la sección `require`. Mientras que el primer número especifica la versión para la que se instalarán los paquetes, el segundo indica para qué versión está escrita la aplicación. +(Por supuesto, no tiene sentido que estas versiones sean diferentes, así que la doble entrada es una redundancia). Esta versión se establece con el comando + +```shell +composer require php 8.2.3 --no-update +``` + +O directamente en el archivo `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Informes falsos .[#toc-false-reports] +===================================== + +Cuando se actualizan paquetes o se cambian los números de versión, surgen conflictos. Un paquete tiene requisitos que entran en conflicto con otro y así sucesivamente. Sin embargo, Composer ocasionalmente imprime un mensaje falso. Informa de un conflicto que realmente no existe. En este caso, ayuda borrar el archivo `composer.lock` e intentarlo de nuevo. + +Si el mensaje de error persiste, entonces va en serio y hay que leer en él qué modificar y cómo. + Packagist.org - Repositorio global .[#toc-packagist-org-global-repository] ========================================================================== diff --git a/best-practices/fr/composer.texy b/best-practices/fr/composer.texy index 1b7fcc58e3..485cd78e67 100644 --- a/best-practices/fr/composer.texy +++ b/best-practices/fr/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Mise à jour de la dernière version .[#toc-update-to-the-latest-version] -======================================================================= +Mise à jour des paquets vers les dernières versions .[#toc-update-packages-to-the-latest-versions] +================================================================================================== Pour mettre à jour tous les paquets utilisés à la dernière version selon les contraintes de version définies dans `composer.json`, utilisez la commande `composer update`. Par exemple, pour la dépendance `"nette/database": "^3.0"`, elle installera la dernière version 3.x.x, mais pas la version 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project A la place de `name-of-the-project`, vous devez fournir le nom du répertoire de votre projet et exécuter la commande. Composer ira chercher le dépôt `nette/web-project` de GitHub, qui contient déjà le fichier `composer.json`, et juste après, il installera le Framework Nette lui-même. La seule chose qui reste à faire est de [vérifier les droits d'écriture |nette:troubleshooting#setting-directory-permissions] sur les répertoires `temp/` et `log/` et vous êtes prêt à partir. +Si vous connaissez la version de PHP sur laquelle le projet sera hébergé, assurez-vous de la [configurer |#PHP Version]. + Version PHP .[#toc-php-version] =============================== -Composer installe toujours les versions des paquets qui sont compatibles avec la version de PHP que vous utilisez actuellement. Cette version peut, bien entendu, ne pas être la même que celle de PHP sur votre hôte Web. Il est donc utile d'ajouter au fichier `composer.json` des informations sur la version de PHP sur l'hôte, et seules les versions des paquets compatibles avec l'hôte seront alors installées : +Composer installe toujours les versions des paquets compatibles avec la version de PHP que vous utilisez actuellement (ou plutôt, la version de PHP utilisée en ligne de commande lorsque vous lancez Composer). Ce qui n'est probablement pas la même version que celle utilisée par votre hébergeur. C'est pourquoi il est très important d'ajouter l'information sur la version de PHP de votre hébergement dans votre fichier `composer.json`. Ensuite, seules les versions des paquets compatibles avec l'hébergeur seront installées. + +Par exemple, pour configurer le projet pour qu'il fonctionne avec PHP 8.2.3, utilisez la commande : + +```shell +composer config platform.php 8.2.3 +``` + +C'est ainsi que la version est écrite dans le fichier `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Cependant, le numéro de version de PHP est également indiqué ailleurs dans le fichier, dans la section `require`. Alors que le premier numéro spécifie la version pour laquelle les paquets seront installés, le second numéro indique la version pour laquelle l'application elle-même est écrite. +(Bien sûr, il n'y a pas de sens à ce que ces versions soient différentes, donc la double entrée est une redondance). Vous définissez cette version à l'aide de la commande : + +```shell +composer require php 8.2.3 --no-update +``` + +Ou directement dans le fichier `composer.json` : + +```js +{ + "require" : { + "php" : "8.2.3" + } +} +``` + + +Faux rapports .[#toc-false-reports] +=================================== + +Lors de la mise à niveau de paquets ou du changement de numéro de version, des conflits se produisent. Un paquet a des exigences qui entrent en conflit avec un autre, et ainsi de suite. Cependant, Composer imprime parfois un faux message. Il signale un conflit qui n'existe pas réellement. Dans ce cas, il est utile de supprimer le fichier `composer.lock` et de réessayer. + +Si le message d'erreur persiste, c'est qu'il est sérieux et que vous devez y lire ce qu'il faut modifier et comment. + Packagist.org - Dépôt global .[#toc-packagist-org-global-repository] ==================================================================== diff --git a/best-practices/hu/composer.texy b/best-practices/hu/composer.texy index d2a075e1ba..a4159ed069 100644 --- a/best-practices/hu/composer.texy +++ b/best-practices/hu/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Frissítés a legújabb verzióra .[#toc-update-to-the-latest-version] -================================================================== +Csomagok frissítése a legújabb verziókra .[#toc-update-packages-to-the-latest-versions] +======================================================================================= Az összes használt csomag frissítéséhez a `composer.json` pontban meghatározott verziókövetelményeknek megfelelően a `composer update` paranccsal frissítheti az összes használt csomagot a legújabb verzióra. Például a `"nette/database": "^3.0"` függőség esetében a parancs a legújabb, 3.x.x.x verziót fogja telepíteni, de a 4-es verziót nem. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project A `name-of-the-project` helyett meg kell adnia a projekt könyvtárának nevét, és végre kell hajtania a parancsot. A Composer le fogja hívni a `nette/web-project` tárolót a GitHubról, amely már tartalmazza a `composer.json` fájlt, és rögtön ezután telepíti magát a Nette keretrendszert. Már csak a `temp/` és a `log/` könyvtárak [írási jogosultságainak ellenőrzése |nette:troubleshooting#setting-directory-permissions] van hátra, és már mehet is. +Ha tudod, hogy a PHP melyik verzióján lesz a projekt, mindenképpen [állítsd be |#PHP Version]. + PHP verzió .[#toc-php-version] ============================== -A Composer mindig azokat a csomagverziókat telepíti, amelyek kompatibilisek a PHP aktuálisan használt verziójával. Ami természetesen nem biztos, hogy ugyanaz a verzió, mint a PHP az Ön webtárhelyén. Ezért hasznos, ha a `composer.json` fájlhoz hozzáadjuk a tárhelyen lévő PHP verziójára vonatkozó információt, és akkor csak a tárhelyen lévő csomagoknak a tárhelyhez kompatibilis verziói lesznek telepítve: +A Composer mindig a csomagok azon verzióit telepíti, amelyek kompatibilisek a PHP aktuálisan használt verziójával (vagy inkább a PHP-nak a parancssorban a Composer futtatásakor használt verziójával). Ami valószínűleg nem ugyanaz a verzió, mint amit a webtárhelye használ. Ezért nagyon fontos, hogy a `composer.json` fájlodhoz hozzáadj információt a tárhelyeden használt PHP verziójáról. Ezután csak a tárhelyével kompatibilis csomagok verziói fognak települni. + +Például, ha a projektet a PHP 8.2.3-as verzióján szeretné futtatni, használja a következő parancsot: + +```shell +composer config platform.php 8.2.3 +``` + +Így íródik a verzió a `composer.json` fájlba: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +A PHP verziószáma azonban a fájlban máshol is szerepel, a `require` szakaszban. Míg az első szám azt a verziót adja meg, amelyhez a csomagokat telepíteni kell, addig a második szám azt mondja meg, hogy maga az alkalmazás milyen verzióra íródott. +(Természetesen nincs értelme, hogy ezek a verziók különbözőek legyenek, így a dupla bejegyzés csak redundancia.) Ezt a verziót a paranccsal állítjuk be: + +```shell +composer require php 8.2.3 --no-update +``` + +Vagy közvetlenül a `composer.json` fájlban: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Hamis jelentések .[#toc-false-reports] +====================================== + +A csomagok frissítésekor vagy a verziószámok megváltoztatásakor konfliktusok fordulnak elő. Az egyik csomagnak vannak olyan követelményei, amelyek ütköznek egy másikkal, és így tovább. A Composer azonban időnként hamis üzeneteket ír ki. Olyan konfliktust jelent, amely valójában nem létezik. Ebben az esetben segít, ha törli a `composer.lock` fájlt, és újra megpróbálja. + +Ha a hibaüzenet továbbra is fennáll, akkor komolyan gondolja, és ki kell olvasni belőle, hogy mit és hogyan kell módosítani. + Packagist.org - Globális tárolóhely .[#toc-packagist-org-global-repository] =========================================================================== diff --git a/best-practices/it/composer.texy b/best-practices/it/composer.texy index 4eb632ac29..c30fc6d908 100644 --- a/best-practices/it/composer.texy +++ b/best-practices/it/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Aggiornamento alla versione più recente .[#toc-update-to-the-latest-version] -============================================================================ +Aggiornare i pacchetti alle ultime versioni .[#toc-update-packages-to-the-latest-versions] +========================================================================================== Per aggiornare tutti i pacchetti utilizzati all'ultima versione in base ai vincoli di versione definiti in `composer.json`, usare il comando `composer update`. Ad esempio, per la dipendenza `"nette/database": "^3.0"` verrà installata l'ultima versione 3.x.x, ma non la versione 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Al posto di `name-of-the-project` si deve fornire il nome della directory del progetto ed eseguire il comando. Composer recupererà il repository `nette/web-project` da GitHub, che contiene già il file `composer.json`, e subito dopo installerà il framework Nette. L'unica cosa che resta da fare è [controllare i permessi di scrittura |nette:troubleshooting#setting-directory-permissions] sulle directory `temp/` e `log/` e il gioco è fatto. +Se si conosce la versione di PHP su cui verrà ospitato il progetto, assicurarsi di [impostarla |#PHP Version]. + Versione PHP .[#toc-php-version] ================================ -Composer installa sempre le versioni dei pacchetti compatibili con la versione di PHP attualmente in uso. Che, ovviamente, potrebbe non essere la stessa versione di PHP presente sul vostro host web. Per questo motivo, è utile aggiungere al file `composer.json` informazioni sulla versione di PHP presente sull'host, in modo da installare solo le versioni dei pacchetti compatibili con l'host: +Composer installa sempre le versioni dei pacchetti compatibili con la versione di PHP attualmente in uso (o meglio, la versione di PHP utilizzata dalla riga di comando quando si esegue Composer). Che probabilmente non è la stessa versione utilizzata dal vostro host web. Per questo motivo è molto importante aggiungere al file `composer.json` le informazioni sulla versione di PHP presente sul vostro hosting. In questo modo, verranno installate solo le versioni dei pacchetti compatibili con l'host. + +Per esempio, per impostare il progetto in modo che venga eseguito su PHP 8.2.3, usare il comando: + +```shell +composer config platform.php 8.2.3 +``` + +In questo modo la versione viene scritta nel file `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Tuttavia, il numero di versione di PHP è elencato anche altrove nel file, nella sezione `require`. Mentre il primo numero specifica la versione per la quale verranno installati i pacchetti, il secondo numero dice per quale versione è stata scritta l'applicazione stessa. +(Naturalmente, non ha senso che queste versioni siano diverse, quindi la doppia indicazione è una ridondanza). La versione viene impostata con il comando: + +```shell +composer require php 8.2.3 --no-update +``` + +Oppure direttamente nel file `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Rapporti falsi .[#toc-false-reports] +==================================== + +Quando si aggiornano i pacchetti o si cambiano i numeri di versione, si verificano dei conflitti. Un pacchetto ha requisiti in conflitto con un altro e così via. Tuttavia, Composer a volte stampa dei falsi messaggi. Segnala un conflitto che in realtà non esiste. In questo caso, è utile cancellare il file `composer.lock` e riprovare. + +Se il messaggio di errore persiste, allora è da intendersi seriamente e bisogna leggere da esso cosa modificare e come. + Packagist.org - Repository globale .[#toc-packagist-org-global-repository] ========================================================================== diff --git a/best-practices/pl/composer.texy b/best-practices/pl/composer.texy index 5b3cbade87..f208932801 100644 --- a/best-practices/pl/composer.texy +++ b/best-practices/pl/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Aktualizacja do najnowszych wersji .[#toc-update-to-the-latest-version] -======================================================================= +Aktualizacja pakietów do najnowszych wersji .[#toc-update-packages-to-the-latest-versions] +========================================================================================== Polecenie `composer update` jest odpowiedzialne za aktualizację używanych bibliotek do najnowszych wersji zgodnie z warunkami określonymi w `composer.json`. Na przykład dla zależności `"nette/database": "^3.0"` zainstaluje najnowszą wersję 3.x.x, ale już nie wersję 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project nazev-projektu Wpisz nazwę katalogu dla swojego projektu jako `nazev-projektu` i zatwierdź. Composer pobiera z GitHuba repozytorium `nette/web-project`, które zawiera już `composer.json`, a następnie Nette Framework. Powinieneś tylko [ustawić uprawnienia |nette:troubleshooting#Setting-Directory-Permissions] do [zapisu |nette:troubleshooting#Setting-Directory-Permissions] na `temp/` i `log/` i twój projekt powinien ożyć. +Jeśli wiesz, na jakiej wersji PHP będzie hostowany projekt, koniecznie [go |#PHP Version] skonfiguruj. + Wersja PHP .[#toc-php-version] ============================== -Composer zawsze instaluje wersje pakietów, które są zgodne z wersją PHP, której aktualnie używasz. Co oczywiście może nie być tą samą wersją, której używa twój hosting. Dlatego warto dodać do pliku `composer.json` informację o wersji PHP na Twoim hostingu, a wtedy zainstalowane zostaną tylko wersje pakietów zgodne z Twoim hostingiem: +Composer zawsze instaluje wersje pakietów, które są kompatybilne z wersją PHP, której aktualnie używasz (lub raczej z wersją PHP używaną w linii poleceń, gdy uruchamiasz Composera). Co prawdopodobnie nie jest tą samą wersją, której używa Twój hosting. Dlatego bardzo ważne jest dodanie informacji o wersji PHP na Twoim hostingu do pliku `composer.json`. Następnie zostaną zainstalowane tylko wersje pakietów zgodne z hostem. + +Na przykład, aby ustawić projekt tak, aby działał na PHP 8.2.3, użyj polecenia: + +```shell +composer config platform.php 8.2.3 +``` + +W ten sposób wersja jest zapisywana do pliku `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # verze PHP na hostingu + "php": "8.2.3" } } } ``` +Jednakże, numer wersji PHP jest również podany w innym miejscu pliku, w sekcji `require`. Podczas gdy pierwszy numer określa wersję, dla której zostaną zainstalowane pakiety, drugi mówi, dla jakiej wersji została napisana sama aplikacja. +(Oczywiście nie ma sensu, aby te wersje były różne, więc podwójny wpis jest redundancją). Wersję tę ustawiasz poleceniem: + +```shell +composer require php 8.2.3 --no-update +``` + +Lub bezpośrednio w pliku `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Fałszywe raporty .[#toc-false-reports] +====================================== + +Podczas aktualizacji pakietów lub zmiany numerów wersji zdarzają się konflikty. Jeden pakiet ma wymagania, które są sprzeczne z innym i tak dalej. Jednakże, Composer czasami drukuje fałszywe wiadomości. Zgłasza konflikt, który tak naprawdę nie istnieje. W takim przypadku pomaga usunięcie pliku `composer.lock` i ponowna próba. + +Jeśli komunikat o błędzie utrzymuje się, to znaczy, że jest on poważny i trzeba z niego wyczytać, co i jak należy zmodyfikować. + Packagist.org - centralne repozytorium .[#toc-packagist-org-global-repository] ============================================================================== diff --git a/best-practices/pt/composer.texy b/best-practices/pt/composer.texy index a15e55100d..d6f7112628 100644 --- a/best-practices/pt/composer.texy +++ b/best-practices/pt/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Atualização para a versão mais recente .[#toc-update-to-the-latest-version] -=========================================================================== +Pacotes de atualização para as versões mais recentes .[#toc-update-packages-to-the-latest-versions] +=================================================================================================== Para atualizar todos os pacotes usados para a versão mais recente de acordo com as restrições de versão definidas em `composer.json` use o comando `composer update`. Por exemplo, para a dependência `"nette/database": "^3.0"` será instalada a versão mais recente 3.x.x, mas não a versão 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Em vez disso, o `name-of-the-project` você deve fornecer o nome do diretório para seu projeto e executar o comando. O compositor buscará o repositório `nette/web-project` no GitHub, que já contém o arquivo `composer.json`, e logo em seguida instalará o próprio Nette Framework. A única coisa que resta é [verificar as permissões de escrita |nette:troubleshooting#setting-directory-permissions] nos diretórios `temp/` e `log/` e você está pronto para ir. +Se você sabe em que versão do PHP o projeto será hospedado, não deixe de [configurá-lo |#PHP Version]. + Versão PHP .[#toc-php-version] ============================== -O Composer sempre instala aquelas versões de pacotes que são compatíveis com a versão do PHP que você está usando atualmente. Que, é claro, pode não ser a mesma versão do PHP em seu web host. Portanto, é útil adicionar informações sobre a versão do PHP no host ao arquivo `composer.json`, e então somente as versões dos pacotes compatíveis com o host serão instaladas: +O Composer sempre instala as versões dos pacotes que são compatíveis com a versão do PHP que você está usando atualmente (ou melhor, a versão do PHP usada na linha de comando quando você executa o Composer). Que provavelmente não é a mesma versão que seu web host está usando. É por isso que é muito importante adicionar informações sobre a versão do PHP em sua hospedagem ao seu arquivo `composer.json`. Depois disso, somente versões de pacotes compatíveis com o host serão instaladas. + +Por exemplo, para configurar o projeto para rodar no PHP 8.2.3, use o comando: + +```shell +composer config platform.php 8.2.3 +``` + +Esta é a forma como a versão é escrita no arquivo `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +No entanto, o número da versão PHP também está listado em outra parte do arquivo, na seção `require`. Enquanto o primeiro número especifica a versão para a qual os pacotes serão instalados, o segundo número diz para qual versão o aplicativo em si é escrito. +(É claro, não faz sentido que estas versões sejam diferentes, portanto, a entrada dupla é uma redundância). Você define esta versão com o comando: + +```shell +composer require php 8.2.3 --no-update +``` + +Ou diretamente no arquivo `composer.json': + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Falsos relatórios .[#toc-false-reports] +======================================= + +Ao atualizar pacotes ou mudar números de versão, conflitos acontecem. Um pacote tem requisitos que entram em conflito com outro e assim por diante. No entanto, o Composer ocasionalmente imprime uma mensagem falsa. Ele relata um conflito que realmente não existe. Neste caso, ele ajuda a apagar o arquivo `composer.lock` e tenta novamente. + +Se a mensagem de erro persistir, então o objetivo é sério e você precisa ler a partir dela o que modificar e como. + Packagist.org - Repositório Global .[#toc-packagist-org-global-repository] ========================================================================== diff --git a/best-practices/ro/composer.texy b/best-practices/ro/composer.texy index 9579098ee3..ef98ebdb69 100644 --- a/best-practices/ro/composer.texy +++ b/best-practices/ro/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Actualizare la cea mai recentă versiune .[#toc-update-to-the-latest-version] -============================================================================ +Actualizarea pachetelor la cele mai recente versiuni .[#toc-update-packages-to-the-latest-versions] +=================================================================================================== Pentru a actualiza toate pachetele utilizate la cea mai recentă versiune în conformitate cu constrângerile de versiune definite în `composer.json`, utilizați comanda `composer update`. De exemplu, pentru dependența `"nette/database": "^3.0"`, se va instala cea mai recentă versiune 3.x.x, dar nu și versiunea 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project În loc de `name-of-the-project` trebuie să furnizați numele directorului pentru proiectul dumneavoastră și să executați comanda. Composer va prelua depozitul `nette/web-project` de pe GitHub, care conține deja fișierul `composer.json`, și imediat după aceea va instala chiar Nette Framework. Singurul lucru care rămâne este să [verificați permisiunile de scriere |nette:troubleshooting#setting-directory-permissions] pe directoarele `temp/` și `log/` și sunteți gata de plecare. +Dacă știți pe ce versiune de PHP va fi găzduit proiectul, nu uitați să [o configura |#PHP Version]ți. + Versiunea PHP .[#toc-php-version] ================================= -Composer instalează întotdeauna acele versiuni de pachete care sunt compatibile cu versiunea de PHP pe care o utilizați în prezent. Care, bineînțeles, poate să nu fie aceeași versiune cu cea a PHP de pe gazda dvs. web. Prin urmare, este util să adăugați informații despre versiunea PHP de pe gazdă în fișierul `composer.json`, iar apoi vor fi instalate numai versiunile de pachete compatibile cu gazda: +Composer instalează întotdeauna versiunile de pachete care sunt compatibile cu versiunea de PHP pe care o folosiți în prezent (sau, mai degrabă, versiunea de PHP utilizată în linia de comandă atunci când executați Composer). Care, probabil, nu este aceeași versiune pe care o folosește gazda dvs. web. De aceea, este foarte important să adăugați informații despre versiunea PHP de pe gazda dumneavoastră în fișierul `composer.json`. După aceea, vor fi instalate numai versiunile de pachete compatibile cu gazda. + +De exemplu, pentru a seta proiectul să ruleze pe PHP 8.2.3, utilizați comanda: + +```shell +composer config platform.php 8.2.3 +``` + +În acest fel, versiunea este scrisă în fișierul `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Cu toate acestea, numărul versiunii PHP este, de asemenea, listat în altă parte în fișier, în secțiunea `require`. În timp ce primul număr specifică versiunea pentru care vor fi instalate pachetele, al doilea număr indică versiunea pentru care este scrisă aplicația în sine. +(Desigur, nu are sens ca aceste versiuni să fie diferite, așa că dubla înscriere este o redundanță). Setați această versiune cu ajutorul comenzii: + +```shell +composer require php 8.2.3 --no-update +``` + +Sau direct în fișierul `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Rapoarte false .[#toc-false-reports] +==================================== + +La actualizarea pachetelor sau la schimbarea numerelor de versiune, apar conflicte. Un pachet are cerințe care intră în conflict cu un alt pachet și așa mai departe. Cu toate acestea, Composer tipărește ocazional un mesaj fals. Acesta raportează un conflict care nu există cu adevărat. În acest caz, este util să ștergeți fișierul `composer.lock` și să încercați din nou. + +Dacă mesajul de eroare persistă, atunci acesta are o intenție serioasă și trebuie să citiți din el ce trebuie să modificați și cum. + Packagist.org - Depozitul global .[#toc-packagist-org-global-repository] ======================================================================== diff --git a/best-practices/ru/composer.texy b/best-practices/ru/composer.texy index c01a91dbc6..8b6c3fca4a 100644 --- a/best-practices/ru/composer.texy +++ b/best-practices/ru/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Обновление до последней версии .[#toc-update-to-the-latest-version] -=================================================================== +Обновление пакетов до последних версий .[#toc-update-packages-to-the-latest-versions] +===================================================================================== Для обновления всех используемых пакетов до последней версии в соответствии с ограничениями версий, определенными в файле `composer.json`, используйте команду `composer update`. Например, для зависимости `"nette/database": "^3.0"` будет установлена последняя версия 3.x.x, но не версия 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Вместо `name-of-the-project` укажите имя каталога для вашего проекта и выполните команду. Composer получит репозиторий `nette/web-project` с GitHub, который уже содержит файл `composer.json`, и сразу после этого установит сам фреймворк Nette. Осталось только [проверить права на запись |nette:troubleshooting#Setting-Directory-Permissions] для директорий `temp/` и `log/`, и вы готовы к работе. +Если вы знаете, на какой версии PHP будет размещен проект, обязательно установите [ее |#PHP Version]. + Версия PHP .[#toc-php-version] ============================== -Composer всегда устанавливает те версии пакетов, которые совместимы с версией PHP, используемой вами в данный момент. Это, конечно, может быть не та версия PHP, которая установлена на вашем хосте. Поэтому полезно добавить информацию о версии PHP на хосте в файл `composer.json`, и тогда будут установлены только версии пакетов, совместимые с хостом: +Composer всегда устанавливает версии пакетов, совместимые с версией PHP, которую вы используете в данный момент (точнее, версию PHP, используемую в командной строке при запуске Composer). А это, скорее всего, не та версия, которую использует ваш веб-хост. Поэтому очень важно добавить информацию о версии PHP на вашем хостинге в файл `composer.json`. После этого будут установлены только версии пакетов, совместимые с хостом. + +Например, чтобы настроить проект для работы на PHP 8.2.3, используйте команду: + +```shell +composer config platform.php 8.2.3 +``` + +Таким образом версия записывается в файл `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP версия сервера + "php": "8.2.3" } } } ``` +Однако номер версии PHP также указывается в другом месте файла, в секции `require`. В то время как первое число указывает версию, для которой будут установлены пакеты, второе число говорит о том, для какой версии написано само приложение. +(Конечно, нет смысла в том, чтобы эти версии были разными, поэтому двойная запись является излишеством). Вы устанавливаете эту версию с помощью команды: + +```shell +composer require php 8.2.3 --no-update +``` + +Или непосредственно в файле `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Ложные отчеты .[#toc-false-reports] +=================================== + +При обновлении пакетов или изменении номеров версий возникают конфликты. Один пакет имеет требования, которые конфликтуют с другим и так далее. Однако иногда Composer выдает ложные сообщения. Он сообщает о конфликте, которого на самом деле не существует. В этом случае следует удалить файл `composer.lock` и повторить попытку. + +Если сообщение об ошибке не исчезает, значит, оно имеет серьезное значение, и вам нужно прочитать из него, что и как нужно изменить. + Packagist.org — глобальный репозиторий .[#toc-packagist-org-global-repository] ============================================================================== diff --git a/best-practices/sl/composer.texy b/best-practices/sl/composer.texy index d9c3780059..52eb9cc2da 100644 --- a/best-practices/sl/composer.texy +++ b/best-practices/sl/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Posodobitev na najnovejšo različico .[#toc-update-to-the-latest-version] -======================================================================== +Posodobitev paketov na najnovejše različice .[#toc-update-packages-to-the-latest-versions] +========================================================================================== Za posodobitev vseh uporabljenih paketov na najnovejšo različico v skladu z omejitvami različic, opredeljenimi v `composer.json`, uporabite ukaz `composer update`. Na primer za odvisnost `"nette/database": "^3.0"` bo namestil najnovejšo različico 3.x.x, ne pa tudi različice 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Namesto `name-of-the-project` morate navesti ime imenika za vaš projekt in izvesti ukaz. Composer bo iz GitHuba pobral skladišče `nette/web-project`, ki že vsebuje datoteko `composer.json`, in takoj zatem namestil samo ogrodje Nette. Edina stvar, ki vam preostane, je, da [preverite dovoljenja za pisanje |nette:troubleshooting#setting-directory-permissions] v imenikih `temp/` in `log/`, in že ste pripravljeni za delo. +Če veste, na kateri različici PHP bo projekt gostoval, [jo |#PHP Version] obvezno [nastavite |#PHP Version]. + Različica PHP .[#toc-php-version] ================================= -Composer vedno namesti tiste različice paketov, ki so združljive z različico PHP, ki jo trenutno uporabljate. Ta različica seveda ni nujno enaka različici PHP v vašem spletnem gostitelju. Zato je koristno, da v datoteko `composer.json` dodate informacije o različici PHP na gostitelju, nato pa bodo nameščene samo različice paketov, ki so združljive z gostiteljem: +Composer vedno namesti različice paketov, ki so združljive z različico PHP, ki jo trenutno uporabljate (oziroma z različico PHP, ki je uporabljena v ukazni vrstici, ko zaženete Composer). Ta različica verjetno ni enaka različici, ki jo uporablja vaš spletni gostitelj. Zato je zelo pomembno, da v datoteko `composer.json` dodate informacije o različici PHP na vašem gostovanju. Nato bodo nameščene samo različice paketov, ki so združljive z gostiteljem. + +Na primer, če želite projekt nastaviti tako, da bo deloval na PHP 8.2.3, uporabite ukaz: + +```shell +composer config platform.php 8.2.3 +``` + +Tako se različica zapiše v datoteko `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Vendar je številka različice PHP navedena tudi na drugem mestu v datoteki, v razdelku `require`. Medtem ko prva številka določa različico, za katero bodo nameščeni paketi, druga številka pove, za katero različico je napisana sama aplikacija. +(Seveda ni smiselno, da bi se različici razlikovali, zato je dvojni vpis odveč.) To različico nastavite z ukazom: + +```shell +composer require php 8.2.3 --no-update +``` + +Ali neposredno v datoteki `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Napačna poročila .[#toc-false-reports] +====================================== + +Pri nadgradnji paketov ali spreminjanju številk različic prihaja do konfliktov. En paket ima zahteve, ki so v nasprotju z drugim, in tako naprej. Vendar program Composer občasno izpiše lažna sporočila. Poroča o konfliktu, ki v resnici ne obstaja. V tem primeru pomaga, če izbrišete datoteko `composer.lock` in poskusite znova. + +Če sporočilo o napaki vztraja, je mišljeno resno in iz njega morate razbrati, kaj in kako morate spremeniti. + Packagist.org - Globalni repozitorij .[#toc-packagist-org-global-repository] ============================================================================ diff --git a/best-practices/tr/composer.texy b/best-practices/tr/composer.texy index b134c309b0..74d2c032f1 100644 --- a/best-practices/tr/composer.texy +++ b/best-practices/tr/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -En Son Sürüme Güncelleyin .[#toc-update-to-the-latest-version] -============================================================== +Paketleri En Son Sürümlere Güncelleyin .[#toc-update-packages-to-the-latest-versions] +===================================================================================== Kullanılan tüm paketleri `composer.json` adresinde tanımlanan sürüm kısıtlamalarına göre en son sürüme güncellemek için `composer update` komutunu kullanın. Örneğin `"nette/database": "^3.0"` bağımlılığı için en son 3.x.x sürümünü yükleyecek, ancak sürüm 4'ü yüklemeyecektir. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Bunun yerine `name-of-the-project` adresine projenizin dizininin adını girmeli ve komutu çalıştırmalısınız. Composer, `composer.json` dosyasını zaten içeren GitHub'dan `nette/web-project` deposunu getirecek ve hemen ardından Nette Framework'ün kendisini yükleyecektir. Geriye kalan tek şey `temp/` ve `log/` dizinleri üzerindeki [yazma izinlerini kontrol |nette:troubleshooting#setting-directory-permissions] etmektir ve artık hazırsınız. +Projenin hangi PHP sürümünde barındırılacağını biliyorsanız, [bunu ayarladığınızdan |#PHP Version] emin olun. + PHP Sürümü .[#toc-php-version] ============================== -Composer her zaman kullanmakta olduğunuz PHP sürümü ile uyumlu olan paket sürümlerini yükler. Elbette bu, web barındırıcınızdaki PHP ile aynı sürüm olmayabilir. Bu nedenle, `composer.json` dosyasına ana bilgisayardaki PHP sürümü hakkında bilgi eklemek yararlıdır ve ardından yalnızca ana bilgisayarla uyumlu paket sürümleri yüklenecektir: +Composer her zaman kullanmakta olduğunuz PHP sürümüyle (ya da Composer'ı çalıştırdığınızda komut satırında kullanılan PHP sürümüyle) uyumlu olan paket sürümlerini yükler. Bu sürüm muhtemelen web barındırıcınızın kullandığı sürümle aynı değildir. Bu nedenle `composer.json` dosyanıza barındırıcınızdaki PHP sürümü hakkında bilgi eklemeniz çok önemlidir. Bundan sonra, yalnızca ana bilgisayarla uyumlu paket sürümleri yüklenecektir. + +Örneğin, projeyi PHP 8.2.3 üzerinde çalışacak şekilde ayarlamak için şu komutu kullanın: + +```shell +composer config platform.php 8.2.3 +``` + +Sürüm `composer.json` dosyasına bu şekilde yazılır: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP version on host + "php": "8.2.3" } } } ``` +Bununla birlikte, PHP sürüm numarası dosyanın başka bir yerinde, `require` bölümünde de listelenir. İlk numara paketlerin hangi sürüm için yükleneceğini belirtirken, ikinci numara uygulamanın kendisinin hangi sürüm için yazıldığını söyler. +(Tabii ki, bu sürümlerin farklı olması mantıklı değildir, bu nedenle çift giriş bir fazlalıktır). Bu sürümü şu komutla ayarlarsınız: + +```shell +composer require php 8.2.3 --no-update +``` + +Veya doğrudan `composer.json` dosyasında: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Yanlış Raporlar .[#toc-false-reports] +===================================== + +Paketleri yükseltirken veya sürüm numaralarını değiştirirken çakışmalar meydana gelir. Bir paketin gereksinimleri diğeriyle çakışır ve bu böyle devam eder. Ancak, Composer bazen yanlış bir mesaj yazdırır. Gerçekte var olmayan bir çakışma bildirir. Bu durumda, `composer.lock` dosyasını silmek ve tekrar denemek yardımcı olur. + +Hata mesajı devam ederse, ciddi bir mesajdır ve neyi nasıl değiştireceğinizi okumanız gerekir. + Packagist.org - Küresel Depo .[#toc-packagist-org-global-repository] ==================================================================== diff --git a/best-practices/uk/composer.texy b/best-practices/uk/composer.texy index b0f5c66048..2eead46d70 100644 --- a/best-practices/uk/composer.texy +++ b/best-practices/uk/composer.texy @@ -67,8 +67,8 @@ $db = new Nette\Database\Connection('sqlite::memory:'); ``` -Оновлення до останньої версії .[#toc-update-to-the-latest-version] -================================================================== +Оновлення пакетів до останніх версій .[#toc-update-packages-to-the-latest-versions] +=================================================================================== Для оновлення всіх використовуваних пакетів до останньої версії відповідно до обмежень версій, визначених у файлі `composer.json`, використовуйте команду `composer update`. Наприклад, для залежності `"nette/database": "^3.0"` буде встановлено останню версію 3.x.x, але не версію 4. @@ -98,25 +98,57 @@ composer create-project nette/web-project name-of-the-project Замість `name-of-the-project` вкажіть ім'я каталогу для вашого проєкту і виконайте команду. Composer отримає репозиторій `nette/web-project` з GitHub, який вже містить файл `composer.json`, і відразу після цього встановить сам фреймворк Nette. Залишилося тільки [перевірити права на запис |nette:troubleshooting#Setting-Directory-Permissions] для директорій `temp/` і `log/`, і ви готові до роботи. +Якщо ви знаєте, на якій версії PHP буде розміщено проект, обов'язково встановіть [її |#PHP Version]. + Версія PHP .[#toc-php-version] ============================== -Composer завжди встановлює ті версії пакетів, які сумісні з версією PHP, використовуваною вами на даний момент. Це, звичайно, може бути не та версія PHP, яка встановлена на вашому хості. Тому корисно додати інформацію про версію PHP на хості до файлу `composer.json`, і тоді будуть встановлені тільки версії пакетів, сумісні з хостом: +Composer завжди встановлює версії пакунків, сумісні з версією PHP, яку ви зараз використовуєте (точніше, з версією PHP, яка використовується у командному рядку під час запуску Composer). А це, ймовірно, не та версія, яку використовує ваш веб-хостинг. Ось чому дуже важливо додати інформацію про версію PHP на вашому хостингу до файлу `composer.json`. Після цього будуть встановлені лише версії пакунків, сумісні з хостом. + +Наприклад, щоб налаштувати роботу проекту на PHP 8.2.3, скористайтеся командою: + +```shell +composer config platform.php 8.2.3 +``` + +Таким чином версія буде записана у файл `composer.json`: ```js { - "require": { - ... - }, "config": { "platform": { - "php": "7.2" # PHP версия сервера + "php": "8.2.3" } } } ``` +Однак, номер версії PHP також вказано в іншому місці файлу, в розділі `require`. У той час як перше число вказує на версію, для якої будуть встановлені пакунки, друге число вказує на версію, для якої написано саму програму. +(Звичайно, немає сенсу, щоб ці версії відрізнялися, тому подвійний запис є надмірністю). Ви встановлюєте цю версію за допомогою команди: + +```shell +composer require php 8.2.3 --no-update +``` + +Або безпосередньо у файлі `composer.json`: + +```js +{ + "require": { + "php": "8.2.3" + } +} +``` + + +Неправдиві повідомлення .[#toc-false-reports] +============================================= + +При оновленні пакунків або зміні номерів версій виникають конфлікти. Один пакунок має вимоги, які конфліктують з іншим і так далі. Утім, іноді Composer видає хибні повідомлення. Він повідомляє про конфлікт, якого насправді не існує. У цьому випадку рекомендується видалити файл `composer.lock` і спробувати ще раз. + +Якщо повідомлення про помилку не зникає, це означає, що проблема серйозна, і вам потрібно прочитати, що і як потрібно змінити. + Packagist.org - глобальний репозиторій .[#toc-packagist-org-global-repository] ============================================================================== From 38c3fa90660b23d8c7021ca6d0882cc8d2fd239f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 20 Mar 2023 03:14:05 +0100 Subject: [PATCH 05/47] contributing/documentation: improved --- contributing/bg/documentation.texy | 80 ++++++++++++++++++------------ contributing/cs/documentation.texy | 78 +++++++++++++++++------------ contributing/de/documentation.texy | 74 ++++++++++++++++----------- contributing/el/documentation.texy | 76 +++++++++++++++++----------- contributing/en/documentation.texy | 74 ++++++++++++++++----------- contributing/es/documentation.texy | 76 +++++++++++++++++----------- contributing/fr/documentation.texy | 74 ++++++++++++++++----------- contributing/hu/documentation.texy | 80 ++++++++++++++++++------------ contributing/it/documentation.texy | 76 +++++++++++++++++----------- contributing/pl/documentation.texy | 76 ++++++++++++++++------------ contributing/pt/documentation.texy | 76 +++++++++++++++++----------- contributing/ro/documentation.texy | 80 ++++++++++++++++++------------ contributing/ru/documentation.texy | 76 +++++++++++++++++----------- contributing/sl/documentation.texy | 76 +++++++++++++++++----------- contributing/tr/documentation.texy | 76 +++++++++++++++++----------- contributing/uk/documentation.texy | 76 +++++++++++++++++----------- 16 files changed, 738 insertions(+), 486 deletions(-) diff --git a/contributing/bg/documentation.texy b/contributing/bg/documentation.texy index 9da3087e70..309d6cd434 100644 --- a/contributing/bg/documentation.texy +++ b/contributing/bg/documentation.texy @@ -1,53 +1,69 @@ -Писане на документацията -************************ +Принос към документацията +************************* .[perex] -Приносът към документацията е един от многото начини, по които можете да помогнете на Nette. Това е и една от най-удовлетворяващите дейности, тъй като помагате на другите да разберат рамката. +Приносът към документацията е една от най-ценните дейности, тъй като помага на другите да разберат рамката. -Как да пиша? .[#toc-how-to-write] ---------------------------------- +Как да пишем? .[#toc-how-to-write] +---------------------------------- -Документацията е предназначена предимно за хора, които тепърва се запознават с темата. Затова тя трябва да отговаря на няколко важни момента: +Документацията е предназначена предимно за хора, които са нови по темата. Затова тя трябва да отговаря на няколко важни момента: -- **Когато пишете, започвайте с прости и общи неща, а накрая преминете към по-напреднали теми**. -- Предоставяйте само информацията, която потребителят наистина трябва да знае за темата. -- Проверете дали информацията, която предоставяте, е действително вярна. Първо тествайте примера, преди да го дадете. -- Бъдете кратки - съкратете написаното наполовина. А след това не се колебайте да го направите отново. -- Опитайте се да обясните въпроса възможно най-добре. Например, опитайте се първо да обясните темата на колега. +- Започнете с прости и общи теми. В края преминете към по-напреднали теми +- Опитайте се да обясните темата възможно най-ясно. Например, опитайте се първо да обясните темата на колега +- Предоставяйте само информацията, която потребителят действително трябва да знае за дадена тема +- Уверете се, че информацията ви е точна. Тествайте всеки код +- Бъдете кратки - съкратете написаното наполовина. И след това не се колебайте да го направите отново +- Използвайте пестеливо подчертаване, от удебелени шрифтове до рамки като `.[note]` +- Спазвайте [стандарта за кодиране |Coding Standard] в кода -Имайте предвид тези точки по време на целия процес на писане. Документацията е написана на езика [Texy! |https://texy.info], затова научете неговия [синтаксис |syntax]. Можете да използвате редактора на документация на адрес https://editor.nette.org/, за да прегледате статията, докато я пишете. +Също така научете [синтаксиса |syntax]. За предварителен преглед на статията по време на писане можете да използвате [редактора за предварителен преглед |https://editor.nette.org/]. -Наред с общите правила за писане, изброени по-рано, моля, придържайте се към следните: -- Вашият код трябва да е в съответствие със [Стандарта за кодиране |Coding Standard]. -- Напишете имената на променливите, класовете и методите на английски език. -- Пространствата от имена трябва да се споменават само при първото им споменаване. -- Опитайте се да форматирате кода така, че да не се появяват ленти за превъртане. -- Спестете всички видове маркери за подчертаване, от удебелен шрифт до `.[note]` полета. -- От документацията се позовавайте само на документацията или на `www`. +Мутации на езика .[#toc-language-mutations] +------------------------------------------- +Английският е основният език, така че промените трябва да бъдат на английски. Ако английският не е силната ви страна, използвайте [DeepL Translator |https://www.deepl.com/translator] и други ще проверят текста ви. -Структура на документацията .[#toc-documentation-structure] ------------------------------------------------------------ +Преводът на други езици ще бъде извършен автоматично след одобрение и прецизиране на вашата редакция. + + +Тривиални редакции .[#toc-trivial-edits] +---------------------------------------- + +За да допринесете за документацията, трябва да имате акаунт в [GitHub |https://github.com]. -Пълната документация е поместена в GitHub в хранилището [nette/docs |https://github.com/nette/docs]. Това хранилище е разделено на клонове в зависимост от версията на документацията, например клонът `doc-3.1` съдържа документацията за версия 3.1. А след това има клон `nette.org`, който съдържа съдържанието на другите поддомейни на nette.org. +Най-лесният начин да направите малка промяна в документацията е да използвате връзките в края на всяка страница: -След това всеки клон е разделен на няколко папки: +- *Покажи в GitHub* отваря изходната версия на страницата в GitHub. След това просто натиснете бутона `E` и можете да започнете да редактирате (трябва да сте влезли в GitHub) +- *Отваряне на визуализацията* отваря редактор, в който можете веднага да видите окончателния визуален вид -* `cs` и `en`: съдържа файлове с документация за всяка езикова версия -* `files`: изображения, които могат да се вграждат в страниците с документация +Тъй като [редакторът за предварителен преглед |https://editor.nette.org/] няма възможност за запазване на промените директно в GitHub, трябва да копирате изходния текст в клипборда (с помощта на бутона *Копирай в клипборда*) и след това да го поставите в редактора в GitHub. +Под полето за редактиране се намира форма за изпращане. Тук не забравяйте да обобщите накратко и да обясните причината за вашата редакция. След подаване се създава т.нар. заявка за теглене (PR), която може да се редактира допълнително. -Пътят до файл без разширение съответства на URL адреса на страница от документацията. Така например файлът `en/quickstart/single-post.texy` ще има URL адрес `doc.nette.org/en/quickstart/single-post`. +По-големи редакции .[#toc-larger-edits] +--------------------------------------- -Внасяне на .[#toc-contributing] -------------------------------- +По-подходящо е да се запознаете с основите на работата със системата за контрол на версиите Git, вместо да разчитате единствено на интерфейса на GitHub. Ако не сте запознати с Git, можете да се обърнете към [ръководство git - the simple |https://rogerdudler.github.io/git-guide/] и да помислите за използване на някой от многото налични [графични клиенти |https://git-scm.com/downloads/guis]. -За да допринесете за документацията, трябва да имате акаунт в [GitHub |https://github.com] и да знаете основите на Git. Ако не сте запознати с Git, можете да разгледате краткото ръководство: [git - the simple guide (git - простото ръководство) |https://rogerdudler.github.io/git-guide/] или да използвате някой от многото графични инструменти: [GIT - GUI clients |https://git-scm.com/downloads/guis]. +Редактирайте документацията по следния начин: -Можете да правите прости промени директно в интерфейса на GitHub. По-удобно е обаче да създадете разклонение на хранилището [nette/docs |https://github.com/nette/docs] и да го клонирате на компютъра си. След това направете промени в съответния клон, предайте промяната, избутайте я във вашия GitHub и изпратете заявка за изтегляне в оригиналното хранилище `nette/docs`. +1) в GitHub създайте [разклонение на |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] хранилището [nette/docs |https://github.com/nette/docs] +2) [клонирайте |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] това хранилище на вашия компютър +3) след това направете промени в [съответния клон |#Documentation Structure] +4) проверете за допълнителни интервали в текста с помощта на инструмента [Code-Checker |code-checker:] +5) запишете (commit) промените +6) ако сте доволни от промените, изпратете ги в GitHub във вашето разклонение +7) оттам ги изпратете в хранилището `nette/docs`, като създадете [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Обичайно е да получавате коментари с предложения. Следете за предложените промени и ги включвайте. Добавете предложените промени като нови коммити и ги изпратете отново в GitHub. Никога не създавайте нова заявка за привличане само за да промените съществуваща такава. + + +Структура на документацията .[#toc-documentation-structure] +----------------------------------------------------------- -Преди всяка заявка за изтегляне е добре да стартирате [Code-Checker |code-checker:], за да проверите допълнителните бели полета в текста. +Цялата документация се намира в GitHub в хранилището [nette/docs |https://github.com/nette/docs]. Текущата версия се намира в главния клон, а по-старите версии са разположени в клонове като `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Съдържанието на всеки клон е разделено на основни папки, представляващи отделните области на документацията. Например `application/` съответства на https://doc.nette.org/en/application, `latte/` съответства на https://latte.nette.org, и т.н. Всяка от тези папки съдържа подпапки, представляващи езиковите мутации (`cs`, `en`, ...), и по желание подпапка `files` с изображения, които могат да се вмъкват в страниците в документацията. diff --git a/contributing/cs/documentation.texy b/contributing/cs/documentation.texy index 6e51001407..2e4da280da 100644 --- a/contributing/cs/documentation.texy +++ b/contributing/cs/documentation.texy @@ -1,55 +1,69 @@ -Psaní dokumentace -***************** +Jak přispět do dokumentace +************************** .[perex] -Přispívání do dokumentace je jednou z mnoha cest, jak lze pomoci Nette. Zároveň jde také o jednu z nejpřínosnějších činností, neboť pomáháte druhým frameworku porozumět. +Přispívání do dokumentace je jednou z nejpřínosnějších činností, neboť pomáháte druhým porozumět frameworku. Jak psát? --------- -Dokumentace je určena především lidem, kteří se s tématem teprve seznamují. Proto by měla splňovat několik důležitých bodů: +Dokumentace je určena především lidem, kteří se s tématem seznamují. Proto by měla splňovat několik důležitých bodů: -- **Při psaní začněte od jednoduchého a obecného, k pokročilejším tématům přejděte až na konci.** -- Uvádějte jen ty informace, které uživatel skutečně k danému tématu potřebuje vědět. -- Ověřte si, že vaše informace jsou skutečně pravdivé. Před uvedením kódu příklad nejprve vyzkoušejte. -- Buďte struční - co napíšete, zkraťte na polovinu. A pak klidně ještě jednou. -- Snažte se věc co nejlépe vysvětlit. Zkuste například téma nejprve vysvětlit kolegovi. +- Začněte od jednoduchého a obecného. K pokročilejším tématům přejděte až na konci +- Snažte se věc co nejlépe vysvětlit. Zkuste například téma nejprve vysvětlit kolegovi +- Uvádějte jen ty informace, které uživatel skutečně k danému tématu potřebuje vědět +- Ověřte si, že vaše informace jsou skutečně pravdivé. Každý kód otestujte +- Buďte struční - co napíšete, zkraťte na polovinu. A pak klidně ještě jednou +- Šetřete zvýrazňovači všeho druhu, od tučného písma po rámečky jako `.[note]` +- V kódech dodržujte [Coding Standard] -Tyto body mějte na paměti po celou dobu psaní. Další tipy naleznete v článku [Píšeme pro web |https://www.lupa.cz/clanky/piseme-pro-web/]. Dokumentace je psaná v [Texy! |https://texy.info], proto se naučte jeho [syntax]. Pro náhled článku během jeho psaní můžete použít editor dokumentace na adrese [https://editor.nette.org/]. +Osvojte si také [syntax]. Pro náhled článku během jeho psaní můžete použít [editor s náhledem |https://editor.nette.org/]. -Kromě výše uvedených bodů také dodržujte následující zásady: -- Primárním jazykem je angličtina, vaše změny by tedy měly být v obou jazycích. Pokud angličtina není vaší silnou stránkou, použijte [DeepL Translator |https://www.deepl.com/translator] a ostatní vám váš text zkontrolují. -- V textu dokumentace spíše "mykáme" a jsme zdvořilí. -- V příkladech dodržujte [Coding Standard]. -- Názvy proměnných, tříd a metod pište anglicky. -- Namespaces stačí uvádět při první zmínce. -- Kód se snažte formátovat tak, aby se nezobrazovaly posuvníky. -- Šetřete zvýrazňovači všeho druhu, od tučného písma po rámečky `.[note]`. -- Z dokumentace se odkazujte pouze na dokumentaci nebo `www`. +Jazykové mutace +--------------- +Primárním jazykem je angličtina, vaše změny by tedy měly být v češtině i angličtině. Pokud angličtina není vaší silnou stránkou, použijte [DeepL Translator |https://www.deepl.com/translator] a ostatní vám text zkontrolují. -Struktura dokumentace ---------------------- +Překlad do ostatních jazyků bude proveden automaticky po schválení a doladění vaší úpravy. + + +Triviální úpravy +---------------- + +Pro přispívání do dokumentace je nezbytné mít účet na [GitHub|https://github.com]. -Celá dokumentace je umístěna na GitHubu v repositáři [nette/docs |https://github.com/nette/docs]. Tento repositář je rozdělen do větví podle verze dokumentace, například větev `doc-3.1` obsahuje dokumentaci pro verzi 3.1. A dále je tu větev `nette.org`, ve které je obsah ostatních subdomén webu nette.org. +Nejjednodušší způsob, jak provést drobnou změnu v dokumentaci, je využít odkazy na konci každé stránky: -Každá větev je pak rozdělena do několika složek: +- *Ukaž na GitHubu* otevře zdrojovou podobu dané stránky na GitHubu. Poté stačí stisknout tlačítko `E` a můžete začít editovat (je nutné být na GitHubu přihlášený) +- *Otevři náhled* otevře editor, kde rovnou vidíte i výslednou vizuální podobu -* `cs` a `en`: obsahuje soubory dokumentace pro jednotlivé jazykové verze -* `files`: obrázky, které je možné do stránek v dokumentaci vkládat +Protože [editor s náhledem |https://editor.nette.org/] nemá možnost ukládat změny přímo na GitHub, je nutné po dokončení úprav zdrojový text zkopírovat do schránky (tlačítkem *Copy to clipboard*) a poté jej vložit do editoru na GitHubu. +Pod editačním polem je formulář pro odeslání. Zde nezapomeňte stručně shrnout a vysvětlit důvod vaší úpravy. Po odeslání vznikne tzv. pull request (PR), který je možné dále editovat. -Cesta k souboru bez přípony odpovídá URL adrese stránky v dokumentaci. Soubor `cs/quickstart/single-post.texy` tedy bude mít adresu `doc.nette.org/cs/quickstart/single-post`. +Větší úpravy +------------ -Přispívání do dokumentace -------------------------- +Vhodnější, než využít rozhraní GitHubu, je být obeznámen se základy práce s verzovacím systémem Git. Pokud neovládáte práci s Gitem, můžete se podívat na průvodce [git - the simple guide |https://rogerdudler.github.io/git-guide/] a případně využít některého z mnoha [grafických klientů |https://git-scm.com/downloads/guis]. -Pro přispívání do dokumentace je nutné mít účet na [GitHub|https://github.com] a znát základy práce s verzovacím systémem Git. Pokud s gitem nekamarádíte, můžete se podívat na rychlý návod: [git - the simple guide |https://rogerdudler.github.io/git-guide/], nebo si pomoci některým z mnoha grafických nástrojů: [GIT - GUI clients |https://git-scm.com/downloads/guis]. +Dokumentaci upravujte tímto způsobem: -Jednoduché změny můžete provádět přímo v rozhraní GitHubu. Vhodnější je ale vytvořit si fork repositáře [nette/docs |https://github.com/nette/docs] a ten si naklonovat na počítač. Poté v příslušné větvi proveďte změny, změnu commitněte, pushněte do svého repositáře na GitHub a pošlete pull request do původního repositáře `nette/docs`. Pamatujte, že hlavním jazykem dokumentace je angličtina, změny proto provádějte v obou jazycích. +1) na GitHubu si vytvořte [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] repositáře [nette/docs |https://github.com/nette/docs] +2) tento repositář [naklonujete |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] na svůj počítač +3) poté v [příslušné větvi|#Struktura dokumentace] proveďte změny +4) zkontroluje přebytečné mezery v textu pomocí nástroje [Code-Checker |code-checker:] +4) změny uložte (commitněte) +6) pokud jste se změnami spokojeni, odešlete (pushněte) je na GitHub do vašeho forku +7) odtud je odešlete do repositáře `nette/docs` vytvořením [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Je běžné, že budete dostávat komentáře s připomínkami. Sledujte navrhované změny a zapracujte je. Navrhované změny přidejte jako nové commity a znovu odešlete na GitHub. Nikdy nevytvářejte kvůli úpravě pull requestu nový pull request. + + +Struktura dokumentace +--------------------- -Před každým pull requestem je dobré si spustit [Code-Checker |code-checker:], který nám zkontroluje přebytečné mezery v textu. +Celá dokumentace je umístěna na GitHubu v repositáři [nette/docs |https://github.com/nette/docs]. Aktuální verze je v masteru, starší verze jsou umístěny ve větvích jako `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Obsah každé větve se dělí do hlavních složek představujících jednotlivé oblasti dokumentace. Například `application/` odpovídá https://doc.nette.org/cs/application, `latte/` odpovídá https://latte.nette.org atd. Každá tato složka obsahuje podsložky představující jazykové mutace (`cs`, `en`, ...) a případně podsložku `files` s obrázky, které je možné do stránek v dokumentaci vkládat. diff --git a/contributing/de/documentation.texy b/contributing/de/documentation.texy index 296f6893c1..4c925709ec 100644 --- a/contributing/de/documentation.texy +++ b/contributing/de/documentation.texy @@ -1,53 +1,69 @@ -Verfassen der Dokumentation +Zur Dokumentation beitragen *************************** .[perex] -Die Mitarbeit an der Dokumentation ist eine der vielen Möglichkeiten, wie Sie Nette helfen können. Es ist auch eine der lohnendsten Aktivitäten, da Sie anderen helfen, das Framework zu verstehen. +Die Mitarbeit an der Dokumentation ist eine der wertvollsten Aktivitäten, da sie anderen hilft, den Rahmen zu verstehen. Wie schreibt man? .[#toc-how-to-write] -------------------------------------- -Die Dokumentation ist in erster Linie für Personen gedacht, die sich gerade erst mit dem Thema vertraut machen. Daher sollte sie einige wichtige Punkte erfüllen: +Die Dokumentation richtet sich in erster Linie an Personen, die mit dem Thema noch nicht vertraut sind. Daher sollte sie mehrere wichtige Punkte erfüllen: -- **Beginnen Sie beim Schreiben mit dem Einfachen und Allgemeinen und gehen Sie am Ende zu fortgeschritteneren Themen über.** -- Geben Sie nur die Informationen, die der Benutzer wirklich über das Thema wissen muss. -- Vergewissern Sie sich, dass Ihre Informationen tatsächlich wahr sind. Testen Sie das Beispiel zuerst, bevor Sie es geben. -- Seien Sie kurz und bündig - kürzen Sie das, was Sie schreiben, auf die Hälfte. Und dann können Sie es gerne wiederholen. -- Versuchen Sie, das Thema so gut wie möglich zu erklären. Versuchen Sie zum Beispiel, das Thema zuerst einem Kollegen zu erklären. +- Beginnen Sie mit einfachen und allgemeinen Themen. Gehen Sie am Ende zu fortgeschritteneren Themen über +- Versuchen Sie, das Thema so klar wie möglich zu erklären. Versuchen Sie zum Beispiel, das Thema zuerst einem Kollegen zu erklären. +- Geben Sie nur die Informationen, die der Benutzer für ein bestimmtes Thema tatsächlich benötigt +- Stellen Sie sicher, dass Ihre Informationen korrekt sind. Testen Sie jeden Code +- Seien Sie prägnant - kürzen Sie, was Sie schreiben, um die Hälfte. Und dann können Sie es gerne wiederholen +- Verwenden Sie Hervorhebungen sparsam, von fetten Schriftarten bis hin zu Rahmen wie `.[note]` +- Befolgen Sie den [Kodierungsstandard |Coding Standard] im Code -Behalten Sie diese Punkte während des gesamten Schreibprozesses im Hinterkopf. Die Dokumentation ist in [Texy! |https://texy.info] geschrieben, lernen Sie also dessen [Syntax |syntax]. Sie können den Dokumentationseditor auf https://editor.nette.org/ verwenden, um den Artikel während des Schreibens in der Vorschau anzuzeigen. +Lernen Sie auch die [Syntax |syntax]. Für eine Vorschau des Artikels während des Schreibens, können Sie die [Vorschau-Editor |https://editor.nette.org/] verwenden. -Neben den oben aufgeführten allgemeinen Schreibregeln sollten Sie sich auch an die folgenden halten: -- Ihr Code sollte mit dem [Coding Standard |Coding Standard] übereinstimmen. -- Schreiben Sie die Namen von Variablen, Klassen und Methoden in Englisch. -- Namensräume müssen nur bei der ersten Erwähnung genannt werden. -- Versuchen Sie, den Code so zu formatieren, dass keine Bildlaufleisten angezeigt werden. -- Sparen Sie sich alle Arten von Hervorhebungen, von Fettschrift bis zu `.[note]` Kästen. -- Verweisen Sie in der Dokumentation nur auf die Dokumentation oder `www`. +Sprachmutationen .[#toc-language-mutations] +------------------------------------------- +Englisch ist die Hauptsprache, daher sollten Ihre Änderungen auf Englisch erfolgen. Wenn Englisch nicht Ihre Stärke ist, verwenden Sie [DeepL Translator |https://www.deepl.com/translator] und andere werden Ihren Text überprüfen. -Aufbau der Dokumentation .[#toc-documentation-structure] --------------------------------------------------------- +Die Übersetzung in andere Sprachen erfolgt automatisch nach der Genehmigung und Feinabstimmung Ihrer Bearbeitung. -Die vollständige Dokumentation wird auf GitHub im Repository [nette/docs |https://github.com/nette/docs] gehostet. Dieses Repository ist in Zweige unterteilt, die auf der Version der Dokumentation basieren, z. B. enthält der Zweig `doc-3.1` die Dokumentation für Version 3.1. Und dann gibt es noch den Zweig `nette.org`, der den Inhalt der anderen Subdomains von nette.org enthält. -Jeder Zweig ist dann in mehrere Ordner unterteilt: +Triviale Bearbeitungen .[#toc-trivial-edits] +-------------------------------------------- -* `cs` und `en`: enthält die Dokumentationsdateien für jede Sprachversion -* `files`: Bilder, die in die Dokumentationsseiten eingebettet werden können +Um zur Dokumentation beizutragen, müssen Sie ein Konto auf [GitHub |https://github.com] haben. -Der Pfad zu einer Datei ohne Erweiterung entspricht der URL einer Seite in der Dokumentation. So hat die Datei `en/quickstart/single-post.texy` die URL `doc.nette.org/en/quickstart/single-post`. +Der einfachste Weg, eine kleine Änderung an der Dokumentation vorzunehmen, ist die Verwendung der Links am Ende jeder Seite: +- *Auf GitHub anzeigen* öffnet die Quellversion der Seite auf GitHub. Klicken Sie dann einfach auf die Schaltfläche `E` und Sie können mit der Bearbeitung beginnen (Sie müssen bei GitHub angemeldet sein) +- *Vorschau öffnen* öffnet einen Editor, in dem Sie sofort die endgültige visuelle Form sehen können -Beitrag von .[#toc-contributing] --------------------------------- +Da der [Vorschau-Editor |https://editor.nette.org/] nicht die Möglichkeit bietet, Änderungen direkt auf GitHub zu speichern, müssen Sie den Quelltext in die Zwischenablage kopieren (mit der Schaltfläche *In die Zwischenablage kopieren*) und dann in den Editor auf GitHub einfügen. +Unterhalb des Bearbeitungsfeldes befindet sich ein Formular zum Einreichen. Vergessen Sie hier nicht, den Grund für Ihre Bearbeitung kurz zusammenzufassen und zu erläutern. Nach dem Einreichen wird ein sogenannter Pull Request (PR) erstellt, der weiter bearbeitet werden kann. -Um zur Dokumentation beizutragen, müssen Sie ein Konto bei [GitHub |https://github.com] haben und die Grundlagen von Git kennen. Wenn Sie mit Git nicht vertraut sind, können Sie sich die Kurzanleitung ansehen: [git - the simple guide |https://rogerdudler.github.io/git-guide/], oder eines der vielen grafischen Tools verwenden: [GIT - GUI-Clients |https://git-scm.com/downloads/guis]. -Sie können einfache Änderungen direkt in der GitHub-Oberfläche vornehmen. Es ist jedoch bequemer, einen Fork des Repository [nette/docs |https://github.com/nette/docs] zu erstellen und es auf Ihren Computer zu klonen. Nehmen Sie dann Änderungen im entsprechenden Zweig vor, übertragen Sie die Änderungen, pushen Sie sie auf Ihr GitHub und senden Sie eine Pull-Anfrage an das ursprüngliche Repository `nette/docs`. +Größere Bearbeitungen .[#toc-larger-edits] +------------------------------------------ -Vor jedem Pull-Request ist es eine gute Idee, [Code-Checker |code-checker:] laufen zu lassen, um zusätzliche Leerzeichen im Text zu überprüfen. +Es ist sinnvoller, mit den Grundlagen der Arbeit mit dem Versionskontrollsystem Git vertraut zu sein, als sich ausschließlich auf die GitHub-Schnittstelle zu verlassen. Wenn Sie mit Git nicht vertraut sind, können Sie den Leitfaden [Git - die einfache Anleitung |https://rogerdudler.github.io/git-guide/] zu Rate ziehen und die Verwendung eines der vielen verfügbaren [grafischen Clients |https://git-scm.com/downloads/guis] in Betracht ziehen. -{{priority: -1}} +Bearbeiten Sie die Dokumentation auf die folgende Weise: + +1) Erstellen Sie auf GitHub einen [Fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] des [nette/docs-Repository |https://github.com/nette/docs] +2) [klonen |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] Sie dieses Repository auf Ihren Computer +3) nehmen Sie dann Änderungen im [entsprechenden Zweig |#Documentation Structure]vor +4) Prüfen Sie mit dem [Code-Checker |code-checker:] auf zusätzliche Leerzeichen im Text +5) speichern (committen) Sie die Änderungen +6) wenn du mit den Änderungen zufrieden bist, veröffentliche sie auf GitHub in deinem Fork +7) übermitteln Sie sie von dort aus an das Repository `nette/docs`, indem Sie einen [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) erstellen + +Es ist üblich, dass Sie Kommentare mit Vorschlägen erhalten. Behalten Sie die vorgeschlagenen Änderungen im Auge und nehmen Sie sie auf. Fügen Sie die vorgeschlagenen Änderungen als neue Commits hinzu und senden Sie sie erneut an GitHub. Erstellen Sie niemals einen neuen Pull Request, nur um einen bestehenden zu ändern. + + +Struktur der Dokumentation .[#toc-documentation-structure] +---------------------------------------------------------- + +Die gesamte Dokumentation befindet sich auf GitHub im Repository [nette/docs |https://github.com/nette/docs]. Die aktuelle Version befindet sich im Master-Zweig, während ältere Versionen in Zweigen wie `doc-3.x`, `doc-2.x` zu finden sind. + +Der Inhalt jedes Zweigs ist in Hauptordner unterteilt, die einzelne Bereiche der Dokumentation repräsentieren. So entspricht beispielsweise `application/` dem Ordner https://doc.nette.org/en/application, `latte/` dem Ordner https://latte.nette.org, usw. Jeder dieser Ordner enthält Unterordner für Sprachmutationen (`cs`, `en`, ...) und optional einen Unterordner `files` mit Bildern, die in die Seiten der Dokumentation eingefügt werden können. diff --git a/contributing/el/documentation.texy b/contributing/el/documentation.texy index 315b2a0925..7040e029fe 100644 --- a/contributing/el/documentation.texy +++ b/contributing/el/documentation.texy @@ -1,53 +1,69 @@ -Συγγραφή της τεκμηρίωσης -************************ +Συμβολή στην τεκμηρίωση +*********************** .[perex] -Η συνεισφορά στην τεκμηρίωση είναι ένας από τους πολλούς τρόπους με τους οποίους μπορείτε να βοηθήσετε τη Nette. Είναι επίσης μία από τις πιο ικανοποιητικές δραστηριότητες, καθώς βοηθάτε άλλους να κατανοήσουν το πλαίσιο. +Η συνεισφορά στην τεκμηρίωση είναι μια από τις πιο πολύτιμες δραστηριότητες, καθώς βοηθάει τους άλλους να κατανοήσουν το πλαίσιο. Πώς να γράψετε; .[#toc-how-to-write] ------------------------------------ -Η τεκμηρίωση προορίζεται κυρίως για άτομα που μόλις εξοικειώνονται με το θέμα. Ως εκ τούτου, θα πρέπει να ανταποκρίνεται σε διάφορα σημαντικά σημεία: +Η τεκμηρίωση απευθύνεται κυρίως σε άτομα που είναι καινούργια στο θέμα. Ως εκ τούτου, θα πρέπει να ανταποκρίνεται σε διάφορα σημαντικά σημεία: -- **Όταν γράφετε, ξεκινήστε με τα απλά και γενικά και προχωρήστε σε πιο προχωρημένα θέματα στο τέλος.** -- Παρέχετε μόνο τις πληροφορίες που ο χρήστης χρειάζεται πραγματικά να γνωρίζει για το θέμα. -- Επαληθεύστε ότι οι πληροφορίες σας είναι πράγματι αληθινές. Δοκιμάστε πρώτα το παράδειγμα πριν δώσετε το παράδειγμα. -- Να είστε συνοπτικοί - κόψτε αυτά που γράφετε στη μέση. Και στη συνέχεια μη διστάσετε να το ξανακάνετε. -- Προσπαθήστε να εξηγήσετε το θέμα όσο το δυνατόν καλύτερα. Για παράδειγμα, δοκιμάστε να εξηγήσετε το θέμα πρώτα σε έναν συνάδελφο. +- Ξεκινήστε με απλά και γενικά θέματα. Να προχωράτε σε πιο προχωρημένα θέματα στο τέλος +- Προσπαθήστε να εξηγείτε το θέμα όσο το δυνατόν πιο ξεκάθαρα. Για παράδειγμα, προσπαθήστε να εξηγήσετε το θέμα πρώτα σε έναν συνάδελφο +- Παρέχετε μόνο τις πληροφορίες που πραγματικά χρειάζεται να γνωρίζει ο χρήστης για ένα συγκεκριμένο θέμα +- Βεβαιωθείτε ότι οι πληροφορίες σας είναι ακριβείς. Δοκιμάστε κάθε κώδικα +- Να είστε συνοπτικοί - κόψτε αυτά που γράφετε στη μέση. Και μετά μη διστάσετε να το ξανακάνετε +- Χρησιμοποιήστε με φειδώ την επισήμανση, από έντονες γραμματοσειρές μέχρι πλαίσια όπως `.[note]` +- Ακολουθήστε το [πρότυπο κωδικοποίησης |Coding Standard] στον κώδικα -Έχετε αυτά τα σημεία κατά νου καθ' όλη τη διάρκεια της συγγραφής. Η τεκμηρίωση είναι γραμμένη σε [Texy! |https://texy.info], οπότε μάθετε τη [σύνταξή |syntax] της. Μπορείτε να χρησιμοποιήσετε τον επεξεργαστή τεκμηρίωσης στη διεύθυνση https://editor.nette.org/ για να κάνετε προεπισκόπηση του άρθρου καθώς το γράφετε. +Επίσης, μάθετε τη [σύνταξη |syntax]. Για μια προεπισκόπηση του άρθρου κατά τη διάρκεια της συγγραφής, μπορείτε να χρησιμοποιήσετε τον [επεξεργαστή προεπισκόπησης |https://editor.nette.org/]. -Μεταξύ των γενικών κανόνων συγγραφής που αναφέρθηκαν προηγουμένως, παρακαλούμε να τηρείτε τους ακόλουθους: -- Ο κώδικάς σας θα πρέπει να είναι σύμφωνος με το [Πρότυπο Κωδικοποίησης |Coding Standard]. -- Γράψτε τα ονόματα των μεταβλητών, των κλάσεων και των μεθόδων στα αγγλικά. -- Οι χώροι ονομάτων χρειάζεται να αναφέρονται μόνο κατά την πρώτη αναφορά. -- Προσπαθήστε να μορφοποιήσετε τον κώδικα έτσι ώστε να μην εμφανίζονται μπάρες κύλισης. -- Αποφύγετε κάθε είδους υπογραμμίσεις, από έντονη γραφή έως `.[note]` πλαίσια. -- Από την τεκμηρίωση, ανατρέξτε μόνο στην τεκμηρίωση ή στο `www`. +Μεταλλάξεις της γλώσσας .[#toc-language-mutations] +-------------------------------------------------- +Τα αγγλικά είναι η κύρια γλώσσα, οπότε οι αλλαγές σας θα πρέπει να είναι στα αγγλικά. Αν τα αγγλικά δεν είναι το δυνατό σας σημείο, χρησιμοποιήστε [το DeepL Translator |https://www.deepl.com/translator] και άλλοι θα ελέγξουν το κείμενό σας. -Δομή τεκμηρίωσης .[#toc-documentation-structure] ------------------------------------------------- +Η μετάφραση σε άλλες γλώσσες θα γίνει αυτόματα μετά την έγκριση και την τελειοποίηση της επεξεργασίας σας. + + +Trivial Edits .[#toc-trivial-edits] +----------------------------------- + +Για να συνεισφέρετε στην τεκμηρίωση, πρέπει να έχετε έναν λογαριασμό στο [GitHub |https://github.com]. -Η πλήρης τεκμηρίωση φιλοξενείται στο GitHub στο αποθετήριο [nette/docs |https://github.com/nette/docs]. Αυτό το αποθετήριο χωρίζεται σε κλάδους με βάση την έκδοση της τεκμηρίωσης, για παράδειγμα ο κλάδος `doc-3.1` περιέχει την τεκμηρίωση για την έκδοση 3.1. Και έπειτα υπάρχει ο κλάδος `nette.org`, ο οποίος περιέχει το περιεχόμενο των άλλων υποτομέων του nette.org. +Ο ευκολότερος τρόπος για να κάνετε μια μικρή αλλαγή στην τεκμηρίωση είναι να χρησιμοποιήσετε τους συνδέσμους στο τέλος κάθε σελίδας: -Στη συνέχεια, κάθε κλάδος χωρίζεται σε διάφορους φακέλους: +- *Εμφάνιση στο GitHub* ανοίγει την πηγαία έκδοση της σελίδας στο GitHub. Στη συνέχεια, απλά πατήστε το κουμπί `E` και μπορείτε να ξεκινήσετε την επεξεργασία (πρέπει να είστε συνδεδεμένοι στο GitHub) +- *Άνοιγμα προεπισκόπησης* ανοίγει έναν επεξεργαστή όπου μπορείτε να δείτε αμέσως την τελική οπτική μορφή -* `cs` και `en`: περιέχει αρχεία τεκμηρίωσης για κάθε γλωσσική έκδοση -* `files`: εικόνες που μπορούν να ενσωματωθούν στις σελίδες τεκμηρίωσης +Επειδή ο [επεξεργαστής προεπισκόπησης |https://editor.nette.org/] δεν έχει τη δυνατότητα να αποθηκεύσετε τις αλλαγές απευθείας στο GitHub, πρέπει να αντιγράψετε το πηγαίο κείμενο στο πρόχειρο (χρησιμοποιώντας το κουμπί *Κοπή στο πρόχειρο*) και στη συνέχεια να το επικολλήσετε στον επεξεργαστή στο GitHub. +Κάτω από το πεδίο επεξεργασίας υπάρχει μια φόρμα για την υποβολή. Εδώ, μην ξεχάσετε να συνοψίσετε εν συντομία και να εξηγήσετε τον λόγο της επεξεργασίας σας. Μετά την υποβολή, δημιουργείται ένα λεγόμενο pull request (PR), το οποίο μπορείτε να επεξεργαστείτε περαιτέρω. -Η διαδρομή προς ένα αρχείο χωρίς επέκταση αντιστοιχεί στη διεύθυνση URL μιας σελίδας της τεκμηρίωσης. Έτσι, το αρχείο `en/quickstart/single-post.texy` θα έχει τη διεύθυνση URL `doc.nette.org/en/quickstart/single-post`. +Μεγαλύτερες επεξεργασίες .[#toc-larger-edits] +--------------------------------------------- -Συμβολή .[#toc-contributing] ----------------------------- +Είναι προτιμότερο να είστε εξοικειωμένοι με τα βασικά στοιχεία της εργασίας με το σύστημα ελέγχου εκδόσεων Git παρά να βασίζεστε αποκλειστικά στη διεπαφή του GitHub. Αν δεν είστε εξοικειωμένοι με το Git, μπορείτε να ανατρέξετε στον οδηγό [git - the simple |https://rogerdudler.github.io/git-guide/] και να εξετάσετε το ενδεχόμενο να χρησιμοποιήσετε έναν από τους πολλούς διαθέσιμους [γραφικούς πελάτες |https://git-scm.com/downloads/guis]. -Για να συνεισφέρετε στην τεκμηρίωση, πρέπει να έχετε λογαριασμό στο [GitHub |https://github.com] και να γνωρίζετε τα βασικά του Git. Αν δεν είστε εξοικειωμένοι με το Git, μπορείτε να δείτε τον γρήγορο οδηγό: [git - ο απλός οδηγός |https://rogerdudler.github.io/git-guide/], ή να χρησιμοποιήσετε ένα από τα πολλά γραφικά εργαλεία: [GIT - πελάτες GUI |https://git-scm.com/downloads/guis]. +Επεξεργαστείτε την τεκμηρίωση με τον ακόλουθο τρόπο: -Μπορείτε να κάνετε απλές αλλαγές απευθείας στο περιβάλλον εργασίας του GitHub. Ωστόσο, είναι πιο βολικό να δημιουργήσετε μια διακλάδωση του αποθετηρίου [nette/docs |https://github.com/nette/docs] και να το κλωνοποιήσετε στον υπολογιστή σας. Στη συνέχεια, κάντε αλλαγές στον κατάλληλο κλάδο, δεσμεύστε την αλλαγή, προωθήστε την στο GitHub σας και στείλτε ένα pull request στο αρχικό αποθετήριο `nette/docs`. +1) στο GitHub, δημιουργήστε μια [διακλάδωση |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] του αποθετηρίου [nette/docs |https://github.com/nette/docs] +2) [κλωνοποιήστε |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] αυτό το αποθετήριο στον υπολογιστή σας +3) στη συνέχεια, κάντε αλλαγές στον [κατάλληλο κλάδο |#Documentation Structure] +4) ελέγξτε για επιπλέον κενά στο κείμενο χρησιμοποιώντας το εργαλείο [Code-Checker |code-checker:] +5) αποθηκεύστε (commit) τις αλλαγές +6) αν είστε ικανοποιημένοι με τις αλλαγές, προωθήστε τις στο GitHub στο fork σας +7) από εκεί, υποβάλετε τις στο αποθετήριο `nette/docs` δημιουργώντας ένα [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Είναι σύνηθες να λαμβάνετε σχόλια με προτάσεις. Παρακολουθήστε τις προτεινόμενες αλλαγές και ενσωματώστε τις. Προσθέστε τις προτεινόμενες αλλαγές ως νέες δεσμεύσεις και στείλτε τις εκ νέου στο GitHub. Ποτέ μην δημιουργείτε ένα νέο pull request μόνο και μόνο για να τροποποιήσετε ένα υπάρχον. + + +Δομή τεκμηρίωσης .[#toc-documentation-structure] +------------------------------------------------ -Πριν από κάθε pull request, καλό είναι να τρέχετε το [Code-Checker |code-checker:] για να ελέγχετε τα επιπλέον κενά στο κείμενο. +Ολόκληρη η τεκμηρίωση βρίσκεται στο GitHub στο αποθετήριο [nette/docs |https://github.com/nette/docs]. Η τρέχουσα έκδοση βρίσκεται στον κλάδο master, ενώ παλαιότερες εκδόσεις βρίσκονται σε κλάδους όπως `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Το περιεχόμενο κάθε κλάδου χωρίζεται σε κύριους φακέλους που αντιπροσωπεύουν μεμονωμένες περιοχές της τεκμηρίωσης. Για παράδειγμα, το `application/` αντιστοιχεί στο https://doc.nette.org/en/application, το `latte/` αντιστοιχεί στο https://latte.nette.org, κ.λπ. Κάθε ένας από αυτούς τους φακέλους περιέχει υποφακέλους που αντιπροσωπεύουν γλωσσικές μεταλλάξεις (`cs`, `en`, ...) και προαιρετικά έναν υποφάκελο `files` με εικόνες που μπορούν να εισαχθούν στις σελίδες της τεκμηρίωσης. diff --git a/contributing/en/documentation.texy b/contributing/en/documentation.texy index 5b0b12ce2f..0aec6761dc 100644 --- a/contributing/en/documentation.texy +++ b/contributing/en/documentation.texy @@ -1,53 +1,69 @@ -Writing the Documentation -************************* +Contributing to Documentation +***************************** .[perex] -Contributing to the documentation is one of the many ways you can help Nette. It is also one of the most rewarding activities, as you help others understand the framework. +Contributing to documentation is one of the most valuable activities, as it helps others understand the framework. How to Write? ------------- -The documentation is primarily intended for people who are just getting familiar with the topic. Therefore, it should meet several important points: +Documentation is primarily intended for people who are new to the topic. Therefore, it should meet several important points: -- **When writing, start with the simple and general, and move to more advanced topics at the end.** -- Provide only the information that the user really needs to know about the topic. -- Verify that your information is actually true. Test the example first before giving the example. -- Be concise - cut what you write in half. And then feel free to do it again. -- Try to explain the matter as well as possible. For example, try explaining the topic to a colleague first. +- Start with simple and general topics. Move on to more advanced topics at the end +- Try to explain the topic as clearly as possible. For example, try explaining the topic to a colleague first +- Only provide information that the user actually needs to know for a given topic +- Make sure your information is accurate. Test every code +- Be concise - cut what you write in half. And then feel free to do it again +- Use highlighting sparingly, from bold fonts to frames like `.[note]` +- Follow the [Coding Standard] in the code -Keep these points in mind throughout the writing process. The documentation is written in [Texy! |https://texy.info], so learn its [syntax]. You can use the documentation editor at [https://editor.nette.org/] to preview the article as you write it. +Also, learn the [syntax]. For a preview of the article during writing, you can use the [preview editor |https://editor.nette.org/]. -Among the general writing rules listed earlier, please stick to the following: -- Your code should be in compliance with the [Coding Standard]. -- Write the names of variables, classes and methods in English. -- Namespaces need only be mentioned at first mention. -- Try to format the code so that scroll bars are not displayed. -- Spare all kinds of highlighters, from boldface to `.[note]` boxes. -- From the documentation, refer only to the documentation or `www`. +Language Mutations +------------------ +English is the primary language, so your changes should be in English. If English is not your strong suit, use [DeepL Translator |https://www.deepl.com/translator] and others will check your text. + +Translation into other languages will be done automatically after approval and fine-tuning of your edit. -Documentation Structure ------------------------ -The full documentation is hosted on GitHub in the [nette/docs |https://github.com/nette/docs] repository. This repository is divided into branches based on the version of the documentation, for example the `doc-3.1` branch contains the documentation for version 3.1. And then there is the `nette.org` branch, which contains the content of the other subdomains of nette.org. +Trivial Edits +------------- + +To contribute to the documentation, you need to have an account on [GitHub |https://github.com]. -Each branch is then divided into several folders: +The easiest way to make a small change in the documentation is to use the links at the end of each page: -* `cs` and `en`: contains documentation files for each language version -* `files`: images that can be embedded in the documentation pages +- *Show on GitHub* opens the source version of the page on GitHub. Then just press the `E` button and you can start editing (you must be logged in to GitHub) +- *Open preview* opens an editor where you can immediately see the final visual form -The path to a file without an extension corresponds to the URL of a page in the documentation. Thus, the file `en/quickstart/single-post.texy` will have the URL `doc.nette.org/en/quickstart/single-post`. +Because the [preview editor |https://editor.nette.org/] does not have the ability to save changes directly to GitHub, you need to copy the source text to the clipboard (using the *Copy to clipboard button*) and then paste it into the editor on GitHub. +Below the editing field is a form for submission. Here, don't forget to briefly summarize and explain the reason for your edit. After submitting, a so-called pull request (PR) is created, which can be further edited. -Contributing +Larger Edits ------------ -To contribute to the documentation, you must have an account at [GitHub|https://github.com] and know the basics of Git. If you're not familiar with Git, you can check out the quick guide: [git - the simple guide |https://rogerdudler.github.io/git-guide/], or use one of the many graphical tools: [GIT - GUI clients |https://git-scm.com/downloads/guis]. +It is more appropriate to be familiar with the basics of working with the Git version control system rather than relying solely on the GitHub interface. If you're not familiar with Git, you can refer to the [git - the simple guide |https://rogerdudler.github.io/git-guide/] and consider using one of the many [graphical clients |https://git-scm.com/downloads/guis] available. + +Edit the documentation in the following way: -You can make simple changes directly in the GitHub interface. However, it is more convenient to create a fork of the repository [nette/docs |https://github.com/nette/docs] and clone it to your computer. Then make changes in the appropriate branch, commit the change, push to your GitHub, and send a pull request to the original `nette/docs` repository. +1) on GitHub, create a [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] of the [nette/docs |https://github.com/nette/docs] repository +2) [clone |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] this repository to your computer +3) then, make changes in the [appropriate branch|#Documentation Structure] +4) check for extra spaces in the text using the [Code-Checker |code-checker:] tool +5) save (commit) the changes +6) if you are satisfied with the changes, push them to GitHub to your fork +7) from there, submit them to the `nette/docs` repository by creating a [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +It is common to receive comments with suggestions. Keep track of the proposed changes and incorporate them. Add the suggested changes as new commits and resend them to GitHub. Never create a new pull request just to modify an existing one. + + +Documentation Structure +----------------------- -Before each pull request, it's a good idea to run [Code-Checker |code-checker:] to check extra whitespace in the text. +The entire documentation is located on GitHub in the [nette/docs |https://github.com/nette/docs] repository. The current version is in the master branch, while older versions are located in branches like `doc-3.x`, `doc-2.x`. -{{priority: -1}} +The content of each branch is divided into main folders representing individual areas of documentation. For example, `application/` corresponds to https://doc.nette.org/cs/application, `latte/` corresponds to https://latte.nette.org, etc. Each of these folders contains subfolders representing language mutations (`cs`, `en`, ...) and optionally a `files` subfolder with images that can be inserted into the pages in the documentation. diff --git a/contributing/es/documentation.texy b/contributing/es/documentation.texy index 6474661330..d259f9d79b 100644 --- a/contributing/es/documentation.texy +++ b/contributing/es/documentation.texy @@ -1,53 +1,69 @@ -Redactar la documentación -************************* +Contribuir a la documentación +***************************** .[perex] -Contribuir a la documentación es una de las muchas maneras de ayudar a Nette. También es una de las actividades más gratificantes, ya que ayudas a otros a entender el framework. +Contribuir a la documentación es una de las actividades más valiosas, ya que ayuda a otros a entender el marco. ¿Cómo escribir? .[#toc-how-to-write] ------------------------------------ -La documentación está destinada principalmente a personas que se están familiarizando con el tema. Por lo tanto, debe cumplir varios puntos importantes: +La documentación está destinada principalmente a personas que se inician en el tema. Por lo tanto, debe cumplir varios puntos importantes: -- **Cuando escriba, empiece por lo sencillo y general, y pase a temas más avanzados al final.** -- Proporcione sólo la información que el usuario realmente necesita saber sobre el tema. -- Verifique que su información es realmente cierta. Pruebe primero el ejemplo antes de darlo. -- Sea conciso: reduzca lo que escribe a la mitad. Y luego no dudes en volver a hacerlo. -- Intenta explicar el tema lo mejor posible. Por ejemplo, intenta explicar primero el tema a un colega. +- Empezar con temas sencillos y generales. Pasar al final a temas más avanzados. +- Intente explicar el tema con la mayor claridad posible. Por ejemplo, intente explicárselo primero a un colega. +- Proporcione sólo la información que el usuario realmente necesita saber sobre un tema determinado. +- Asegúrese de que la información es exacta. Pruebe cada código +- Sea conciso: reduzca lo que escribe a la mitad. Y luego no dude en volver a hacerlo. +- Utilice el resaltado con moderación, desde fuentes en negrita hasta marcos como `.[note]` +- Siga la [norma de codificación |Coding Standard] en el código -Tenga en cuenta estos puntos durante todo el proceso de redacción. La documentación está escrita en [Texy! |https://texy.info], así que aprenda su [sintaxis |syntax]. Puedes utilizar el editor de documentación en https://editor.nette.org/ para previsualizar el artículo mientras lo escribes. +Aprenda también la [sintaxis |syntax]. Para obtener una vista previa del artículo durante la escritura, puede utilizar el [editor de vista previa |https://editor.nette.org/]. -Entre las reglas generales de redacción enumeradas anteriormente, respete las siguientes: -- Tu código debe cumplir la [Norma de Codificación |Coding Standard]. -- Escriba los nombres de variables, clases y métodos en inglés. -- Los espacios de nombres sólo deben mencionarse a la primera. -- Intente formatear el código de modo que no aparezcan barras de desplazamiento. -- Prescinda de todo tipo de resaltadores, desde negritas hasta `.[note]` cajas. -- De la documentación, remítase sólo a ella o a `www`. +Mutaciones lingüísticas .[#toc-language-mutations] +-------------------------------------------------- +El inglés es el idioma principal, así que tus cambios deben ser en inglés. Si el inglés no es tu fuerte, utiliza [DeepL Translator |https://www.deepl.com/translator] y otros revisarán tu texto. -Estructura de la documentación .[#toc-documentation-structure] --------------------------------------------------------------- +La traducción a otros idiomas se hará automáticamente tras la aprobación y puesta a punto de tu edición. + + +Ediciones triviales .[#toc-trivial-edits] +----------------------------------------- + +Para contribuir a la documentación, es necesario tener una cuenta en [GitHub |https://github.com]. -La documentación completa está alojada en GitHub en el repositorio [nette/docs |https://github.com/nette/docs]. Este repositorio está dividido en ramas en función de la versión de la documentación, por ejemplo la rama `doc-3.1` contiene la documentación de la versión 3.1. Y luego está la rama `nette.org`, que contiene el contenido de los otros subdominios de nette.org. +La forma más sencilla de hacer un pequeño cambio en la documentación es utilizar los enlaces que aparecen al final de cada página: -Cada rama se divide a su vez en varias carpetas: +- *Mostrar en GitHub* abre la versión fuente de la página en GitHub. A continuación, sólo tienes que pulsar el botón `E` y podrás empezar a editar (debes haber iniciado sesión en GitHub) +- *Abrir vista previa* abre un editor donde puedes ver inmediatamente la forma visual final -* `cs` y `en`: contiene los archivos de documentación de cada versión lingüística -* `files`: imágenes que pueden incrustarse en las páginas de documentación. +Como el editor de vista [previa |https://editor.nette.org/] no tiene la capacidad de guardar los cambios directamente en GitHub, necesitas copiar el texto fuente al portapapeles (usando el botón *Copiar al portapapeles*) y luego pegarlo en el editor en GitHub. +Debajo del campo de edición hay un formulario de envío. Aquí no olvides resumir y explicar brevemente el motivo de tu edición. Tras el envío, se crea una solicitud de extracción (pull request, PR) que puede seguir editándose. -La ruta de un archivo sin extensión corresponde a la URL de una página de la documentación. Así, el fichero `en/quickstart/single-post.texy` tendrá la URL `doc.nette.org/en/quickstart/single-post`. +Ediciones más extensas .[#toc-larger-edits] +------------------------------------------- -Contribución .[#toc-contributing] ---------------------------------- +Es más apropiado estar familiarizado con los fundamentos del trabajo con el sistema de control de versiones Git que confiar únicamente en la interfaz de GitHub. Si no estás familiarizado con Git, puedes consultar la [guía git - the simple guide |https://rogerdudler.github.io/git-guide/] y considerar el uso de uno de los muchos [clientes gráficos |https://git-scm.com/downloads/guis] disponibles. -Para contribuir a la documentación, debes tener una cuenta en [GitHub |https://github.com] y conocer los fundamentos de Git. Si no estás familiarizado con Git, puedes consultar la guía rápida: git - [the simple guide |https://rogerdudler.github.io/git-guide/], o utilizar una de las muchas herramientas gráficas: GIT - [clientes GUI |https://git-scm.com/downloads/guis]. +Edita la documentación de la siguiente manera: -Puedes realizar cambios sencillos directamente en la interfaz de GitHub. Sin embargo, es más conveniente crear un fork del repositorio [nette/docs |https://github.com/nette/docs] y clonarlo en tu ordenador. Luego haz cambios en la rama apropiada, confirma el cambio, empuja a tu GitHub, y envía un pull request al repositorio original `nette/docs`. +1) en GitHub, crea un [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] del repositorio [nette/docs |https://github.com/nette/docs] +2) [clona |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] este repositorio en tu ordenador +3) a continuación, realice cambios en la [rama correspondiente |#Documentation Structure] +4) compruebe si hay espacios de más en el texto utilizando la herramienta [Code-Checker |code-checker:] +5) guarde (confirme) los cambios +6) si estás satisfecho con los cambios, envíalos a tu bifurcación en GitHub +7) desde allí, envíalos al repositorio `nette/docs` creando un [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Es habitual recibir comentarios con sugerencias. No pierdas de vista los cambios propuestos e incorpóralos. Añade los cambios sugeridos como nuevos commits y reenvíalos a GitHub. Nunca crees un nuevo pull request sólo para modificar uno ya existente. + + +Estructura de la documentación .[#toc-documentation-structure] +-------------------------------------------------------------- -Antes de cada pull request, es una buena idea ejecutar [Code-Checker |code-checker:] para comprobar los espacios en blanco sobrantes en el texto. +Toda la documentación se encuentra en GitHub, en el repositorio [nette/docs |https://github.com/nette/docs]. La versión actual se encuentra en la rama maestra, mientras que las versiones anteriores se encuentran en ramas como `doc-3.x`, `doc-2.x`. -{{priority: -1}} +El contenido de cada rama se divide en carpetas principales que representan áreas individuales de la documentación. Por ejemplo, `application/` corresponde a https://doc.nette.org/en/application, `latte/` corresponde a https://latte.nette.org, etc. Cada una de estas carpetas contiene subcarpetas que representan mutaciones lingüísticas (`cs`, `en`, ...) y, opcionalmente, una subcarpeta `files` con imágenes que pueden insertarse en las páginas de la documentación. diff --git a/contributing/fr/documentation.texy b/contributing/fr/documentation.texy index 4eabe728e8..7fa839dfdc 100644 --- a/contributing/fr/documentation.texy +++ b/contributing/fr/documentation.texy @@ -1,53 +1,69 @@ -Rédaction de la documentation +Contribuer à la documentation ***************************** .[perex] -Contribuer à la documentation est l'une des nombreuses façons dont vous pouvez aider Nette. C'est également l'une des activités les plus gratifiantes, car vous aidez les autres à comprendre le framework. +Contribuer à la documentation est l'une des activités les plus utiles, car elle aide les autres à comprendre le cadre. Comment écrire ? .[#toc-how-to-write] ------------------------------------- -La documentation est principalement destinée aux personnes qui commencent à se familiariser avec le sujet. Elle doit donc répondre à plusieurs points importants : +La documentation est principalement destinée aux personnes qui découvrent le sujet. Elle doit donc répondre à plusieurs points importants : -- **Lorsque vous rédigez, commencez par le simple et le général, et passez aux sujets plus avancés à la fin.** -- Ne fournissez que les informations que l'utilisateur a réellement besoin de connaître sur le sujet. -- Vérifiez que vos informations sont réellement vraies. Testez d'abord l'exemple avant de le donner. -- Soyez concis - coupez en deux ce que vous écrivez. Et n'hésitez pas à le faire à nouveau. -- Essayez d'expliquer le sujet aussi bien que possible. Par exemple, essayez d'abord d'expliquer le sujet à un collègue. +- Commencer par des sujets simples et généraux. Passer à des sujets plus avancés à la fin +- Essayez d'expliquer le sujet aussi clairement que possible. Par exemple, essayez d'abord d'expliquer le sujet à un collègue. +- Ne fournissez que les informations que l'utilisateur a réellement besoin de connaître pour un sujet donné. +- Assurez-vous que vos informations sont exactes. Testez chaque code +- Soyez concis - coupez ce que vous écrivez en deux. Et n'hésitez pas à recommencer +- Utilisez la mise en évidence avec parcimonie, qu'il s'agisse de polices en gras ou de cadres tels que `.[note]` +- Respectez la [norme de codage |Coding Standard] dans le code -Gardez ces points à l'esprit tout au long du processus de rédaction. La documentation est écrite en [Texy ! |https://texy.info], apprenez donc sa [syntaxe |syntax]. Vous pouvez utiliser l'éditeur de documentation à l'adresse https://editor.nette.org/ pour prévisualiser l'article à mesure que vous le rédigez. +Apprenez également la [syntaxe |syntax]. Pour avoir un aperçu de l'article en cours de rédaction, vous pouvez utiliser l'[éditeur de prévisualisation |https://editor.nette.org/]. -Parmi les règles générales de rédaction énumérées précédemment, veuillez vous en tenir aux suivantes : -- Votre code doit être conforme à la [norme de codage |Coding Standard]. -- Écrivez les noms des variables, des classes et des méthodes en anglais. -- Les espaces de noms ne doivent être mentionnés qu'à la première mention. -- Essayez de formater le code de manière à ce que les barres de défilement ne s'affichent pas. -- Épargnez toutes sortes de surligneurs, des caractères gras aux `.[note]` boîtes. -- À partir de la documentation, ne faites référence qu'à la documentation ou à `www`. +Mutations linguistiques .[#toc-language-mutations] +-------------------------------------------------- +L'anglais étant la langue principale, vos modifications doivent être rédigées en anglais. Si l'anglais n'est pas votre fort, utilisez [DeepL Translator |https://www.deepl.com/translator] et d'autres personnes vérifieront votre texte. -Structure de la documentation .[#toc-documentation-structure] -------------------------------------------------------------- +La traduction dans d'autres langues se fera automatiquement après l'approbation et la mise au point de votre texte. + + +Modifications mineures .[#toc-trivial-edits] +-------------------------------------------- + +Pour contribuer à la documentation, vous devez avoir un compte sur [GitHub |https://github.com]. -La documentation complète est hébergée sur GitHub dans le dépôt [nette/docs |https://github.com/nette/docs]. Ce dépôt est divisé en branches basées sur la version de la documentation, par exemple la branche `doc-3.1` contient la documentation de la version 3.1. Et puis il y a la branche `nette.org`, qui contient le contenu des autres sous-domaines de nette.org. +La manière la plus simple d'apporter une petite modification à la documentation est d'utiliser les liens qui se trouvent à la fin de chaque page : -Chaque branche est ensuite divisée en plusieurs dossiers : +- *Show on GitHub* ouvre la version source de la page sur GitHub. Il suffit ensuite d'appuyer sur le bouton `E` pour commencer à éditer (vous devez être connecté à GitHub). +- *Open preview* ouvre un éditeur où vous pouvez immédiatement voir la forme visuelle finale. -* `cs` et `en`: contient les fichiers de documentation pour chaque version linguistique -* `files`: images qui peuvent être intégrées dans les pages de documentation +Comme l'[éditeur de prévisualisation |https://editor.nette.org/] ne permet pas d'enregistrer les modifications directement sur GitHub, vous devez copier le texte source dans le presse-papiers (à l'aide du bouton *Copier dans le presse-papiers*) et le coller ensuite dans l'éditeur sur GitHub. +Sous le champ d'édition se trouve un formulaire de soumission. N'oubliez pas de résumer et d'expliquer brièvement la raison de votre modification. Après la soumission, une demande d'extraction (PR) est créée, qui peut être modifiée ultérieurement. -Le chemin d'un fichier sans extension correspond à l'URL d'une page de la documentation. Ainsi, le fichier `en/quickstart/single-post.texy` aura l'URL `doc.nette.org/en/quickstart/single-post`. +Modifications plus importantes .[#toc-larger-edits] +--------------------------------------------------- -Contribuer à .[#toc-contributing] ---------------------------------- +Il est plus approprié de se familiariser avec les bases du travail avec le système de contrôle de version Git plutôt que de se fier uniquement à l'interface GitHub. Si vous n'êtes pas familier avec Git, vous pouvez vous référer à [git - le guide simple |https://rogerdudler.github.io/git-guide/] et envisager d'utiliser l'un des nombreux [clients graphiques |https://git-scm.com/downloads/guis] disponibles. -Pour contribuer à la documentation, vous devez avoir un compte sur [GitHub |https://github.com] et connaître les bases de Git. Si vous n'êtes pas familier avec Git, vous pouvez consulter le guide rapide : [git - the simple guide |https://rogerdudler.github.io/git-guide/], ou utiliser l'un des nombreux outils graphiques : [GIT - GUI clients |https://git-scm.com/downloads/guis]. +Modifiez la documentation de la manière suivante : -Vous pouvez effectuer des modifications simples directement dans l'interface GitHub. Cependant, il est plus pratique de créer un fork du dépôt [nette/docs |https://github.com/nette/docs] et de le cloner sur votre ordinateur. Ensuite, apportez des modifications dans la branche appropriée, livrez les changements, poussez-les vers votre GitHub et envoyez une demande de modification au dépôt original `nette/docs`. +1) sur GitHub, créez une [fourche |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] du dépôt [nette/docs |https://github.com/nette/docs] +2) [clonez |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] ce dépôt sur votre ordinateur +3) ensuite, faites des changements dans la [branche appropriée |#Documentation Structure] +4) vérifiez qu'il n'y a pas d'espaces supplémentaires dans le texte à l'aide de l'outil [Code-Checker |code-checker:] +5) sauvegarder (commit) les changements +6) si vous êtes satisfait des changements, poussez-les sur GitHub vers votre fork +7) à partir de là, soumettez-les au dépôt `nette/docs` en créant une [pull request|https://help.github.com/articles/creating-a-pull-request] (PR). + +Il est courant de recevoir des commentaires contenant des suggestions. Gardez une trace des changements proposés et incorporez-les. Ajoutez les modifications suggérées en tant que nouveaux commits et renvoyez-les à GitHub. Ne créez jamais une nouvelle demande d'extraction juste pour modifier une demande existante. + + +Structure de la documentation .[#toc-documentation-structure] +------------------------------------------------------------- -Avant chaque demande de retrait, il est bon d'exécuter [Code-Checker |code-checker:] pour vérifier les espaces blancs supplémentaires dans le texte. +L'ensemble de la documentation se trouve sur GitHub dans le dépôt [nette/docs |https://github.com/nette/docs]. La version actuelle se trouve dans la branche master, tandis que les versions plus anciennes se trouvent dans des branches telles que `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Le contenu de chaque branche est divisé en dossiers principaux représentant les différents domaines de la documentation. Par exemple, `application/` correspond à https://doc.nette.org/en/application, `latte/` correspond à https://latte.nette.org, etc. Chacun de ces dossiers contient des sous-dossiers représentant les mutations linguistiques (`cs`, `en`, ...) et éventuellement un sous-dossier `files` contenant des images qui peuvent être insérées dans les pages de la documentation. diff --git a/contributing/hu/documentation.texy b/contributing/hu/documentation.texy index 9ceb0dbafd..7e28cbe78c 100644 --- a/contributing/hu/documentation.texy +++ b/contributing/hu/documentation.texy @@ -1,53 +1,69 @@ -A dokumentáció megírása -*********************** +Hozzájárulás a dokumentációhoz +****************************** .[perex] -A dokumentációhoz való hozzájárulás a Nette-nek nyújtott segítség egyik módja. Ez egyben az egyik leghálásabb tevékenység is, mivel segítesz másoknak megérteni a keretrendszert. +A dokumentációhoz való hozzájárulás az egyik legértékesebb tevékenység, mivel segít másoknak megérteni a keretrendszert. -Hogyan kell írni? .[#toc-how-to-write] --------------------------------------- +Hogyan írjunk? .[#toc-how-to-write] +----------------------------------- -A dokumentáció elsősorban azoknak szól, akik még csak most ismerkednek a témával. Ezért több fontos pontnak kell megfelelnie: +A dokumentáció elsősorban azoknak szól, akik újak a témában. Ezért több fontos pontnak kell megfelelnie: -- **Az írás során kezdje az egyszerű és általános témákkal, és a végén térjen át a haladóbb témákra.** -- Csak azokat az információkat adja meg, amelyeket a felhasználónak valóban tudnia kell a témáról. -- Ellenőrizze, hogy az információi valóban igazak-e. Először tesztelje a példát, mielőtt példát adna. -- Legyen tömör - vágja félbe, amit ír. És aztán nyugodtan csináld újra. -- Próbáld meg a lehető legjobban elmagyarázni a dolgot. Próbáld meg például először egy kollégának elmagyarázni a témát. +- Kezdje egyszerű és általános témákkal. A végén térjen át a haladóbb témákra +- Igyekezzen a lehető legvilágosabban elmagyarázni a témát. Például próbálja meg először elmagyarázni a témát egy kollégának. +- Csak olyan információkat adjon meg, amelyeket a felhasználónak valóban tudnia kell az adott témához. +- Győződjön meg arról, hogy az információi pontosak. Teszteljen minden kódot +- Legyen tömör - vágja félbe, amit ír. És aztán nyugodtan csináld újra +- Használjon takarékosan kiemelést, a félkövér betűtípusoktól kezdve a keretekig, mint pl. `.[note]` +- Kövesse a [kódolási szabványt |Coding Standard] a kódban -Ezeket a pontokat tartsa szem előtt az írás során. A dokumentáció [Texy! |https://texy.info] nyelven íródott, ezért tanulja meg a [szintaxisát |syntax]. A https://editor.nette.org/ oldalon található dokumentációs szerkesztővel a cikket írás közben előnézetben megtekintheti. +Tanuld meg a [szintaxist |syntax] is. A cikk írás közbeni előnézetéhez használhatja az [előnézeti szerkesztőt |https://editor.nette.org/]. -A korábban felsorolt általános írási szabályok közül tartsa be a következőket: -- A kódodnak meg kell felelnie a [Kódolási szabványnak |Coding Standard]. -- A változók, osztályok és metódusok nevét angolul írja. -- A névtereket csak első említéskor kell megemlíteni. -- Igyekezzen úgy formázni a kódot, hogy ne jelenjenek meg görgetősávok. -- Kíméljen meg mindenféle kiemeléstől, a félkövér betűtől kezdve a `.[note]` dobozoktól. -- A dokumentációból csak a dokumentációra vagy a `www` címre hivatkozzon. +Nyelvi mutációk .[#toc-language-mutations] +------------------------------------------ +Az angol az elsődleges nyelv, ezért a módosításoknak angolul kell történniük. Ha az angol nem az erősséged, használd a [DeepL fordítót |https://www.deepl.com/translator], és mások ellenőrzik a szövegedet. -A dokumentáció felépítése .[#toc-documentation-structure] ---------------------------------------------------------- +A más nyelvekre történő fordítás automatikusan megtörténik a szerkesztésed jóváhagyása és finomhangolása után. -A teljes dokumentáció a GitHubon található a [nette/docs |https://github.com/nette/docs] tárolóban. Ez a tároló a dokumentáció verziója alapján ágakra van osztva, például a `doc-3.1` ág tartalmazza a 3.1-es verzió dokumentációját. Aztán van a `nette.org` ág, amely a nette.org többi aldomainjének tartalmát tartalmazza. -Az egyes ágak ezután több mappára oszlanak: +Triviális szerkesztések .[#toc-trivial-edits] +--------------------------------------------- -* `cs` és `en`: az egyes nyelvi verziók dokumentációs fájljait tartalmazza. -* `files`: a dokumentációs oldalakba beágyazható képek. +A dokumentációhoz való hozzájáruláshoz egy fiókkal kell rendelkeznie a [GitHubon |https://github.com]. -A kiterjesztés nélküli fájl elérési útja a dokumentáció egy oldalának URL-jének felel meg. Így a `en/quickstart/single-post.texy` fájl URL címe `doc.nette.org/en/quickstart/single-post` lesz. +A legegyszerűbb módja annak, hogy egy apró változtatást végezzünk a dokumentációban, ha az egyes oldalak végén található linkeket használjuk: +- *Megjelenítés a GitHubon* megnyitja az oldal forrásváltozatát a GitHubon. Ezután csak nyomd meg a `E` gombot, és máris kezdheted a szerkesztést (be kell jelentkezned a GitHubra). +- *Előnézet megnyitása* megnyit egy szerkesztőt, ahol azonnal láthatja a végleges vizuális formát -Hozzájárulás .[#toc-contributing] ---------------------------------- +Mivel [az előnézeti szerkesztő |https://editor.nette.org/] nem képes a változtatások közvetlen mentésére a GitHubra, a forrásszöveget a vágólapra kell másolnia (a *Másolás a vágólapra* gomb segítségével), majd beillesztenie a GitHubon lévő szerkesztőbe. +A szerkesztőmező alatt található egy űrlap a beküldéshez. Itt ne felejtse el röviden összefoglalni és megmagyarázni a szerkesztés okát. A beküldés után egy úgynevezett pull request (PR) jön létre, amelyet tovább lehet szerkeszteni. -A dokumentációhoz való hozzájáruláshoz rendelkeznie kell egy fiókkal a [GitHubon |https://github.com], és ismernie kell a Git alapjait. Ha nem ismered a Gitet, akkor nézd meg a gyors útmutatót: [git - az egyszerű útmutató |https://rogerdudler.github.io/git-guide/], vagy használd a számos grafikus eszköz egyikét: [GIT - GUI kliensek |https://git-scm.com/downloads/guis]. -Egyszerű módosításokat közvetlenül a GitHub felületén végezhet. Kényelmesebb azonban, ha létrehoz egy elágazást a [nette/docs |https://github.com/nette/docs] tárolóból, és azt klónozza a számítógépére. Ezután végezzen módosításokat a megfelelő ágban, rögzítse a változást, tolja fel a GitHubra, és küldjön pull requestet az eredeti `nette/docs` tárolóba. +Nagyobb szerkesztések .[#toc-larger-edits] +------------------------------------------ -Minden pull request előtt érdemes lefuttatni a [Code-Checker |code-checker:] programot, hogy ellenőrizze a szövegben lévő extra szóközöket. +Célszerűbb a Git verziókezelő rendszerrel való munka alapjait ismerni, mint kizárólag a GitHub felületére hagyatkozni. Ha nem ismeri a Gitet, olvassa el a [git - az egyszerű útmutatót |https://rogerdudler.github.io/git-guide/], és fontolja meg a számos elérhető [grafikus kliens |https://git-scm.com/downloads/guis] egyikének használatát. -{{priority: -1}} +Szerkessze a dokumentációt a következő módon: + +1) a GitHubon hozzon létre egy [elágazást |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] a [nette/docs |https://github.com/nette/docs] tárolóból. +2) [klónozza |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] ezt a tárolót a számítógépére +3) ezután végezzen módosításokat a [megfelelő ágban |#Documentation Structure] +4) a [Code-Checker |code-checker:] eszközzel ellenőrizze, hogy nincsenek-e extra szóközök a szövegben. +5) mentse (commit) a változtatásokat +6) ha elégedett vagy a változtatásokkal, tedd fel a GitHubra a saját elágazásodba. +7) onnan küldje el őket a `nette/docs` tárolóba egy [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) létrehozásával. + +Gyakori, hogy javaslatokat tartalmazó megjegyzéseket kapunk. Tartsd szemmel a javasolt változtatásokat és építsd be őket. A javasolt változtatásokat új commitként add hozzá, és küldd el újra a GitHubra. Soha ne hozzon létre új pull requestet csak azért, hogy egy meglévőt módosítson. + + +Dokumentáció szerkezete .[#toc-documentation-structure] +------------------------------------------------------- + +A teljes dokumentáció a GitHubon található a [nette/docs |https://github.com/nette/docs] tárolóban. Az aktuális verzió a master ágban található, míg a régebbi verziók a `doc-3.x`, `doc-2.x` ágakban találhatók. + +Az egyes ágak tartalma fő mappákra van osztva, amelyek a dokumentáció egyes területeit képviselik. Például a `application/` megfelel a https://doc.nette.org/en/application, a `latte/` megfelel a https://latte.nette.org, stb. Mindegyik mappa tartalmaz nyelvi mutációkat képviselő almappákat (`cs`, `en`, ...) és opcionálisan egy `files` almappát a dokumentáció oldalaira beilleszthető képekkel. diff --git a/contributing/it/documentation.texy b/contributing/it/documentation.texy index 96c25d9e3c..d1ef26f5d3 100644 --- a/contributing/it/documentation.texy +++ b/contributing/it/documentation.texy @@ -1,53 +1,69 @@ -Scrivere la documentazione -************************** +Contribuire alla documentazione +******************************* .[perex] -Contribuire alla documentazione è uno dei tanti modi in cui si può aiutare Nette. È anche una delle attività più gratificanti, perché aiuta gli altri a comprendere il framework. +Contribuire alla documentazione è una delle attività più preziose, perché aiuta gli altri a comprendere il framework. Come scrivere? .[#toc-how-to-write] ----------------------------------- -La documentazione è destinata principalmente a persone che stanno iniziando a familiarizzare con l'argomento. Pertanto, deve soddisfare diversi punti importanti: +La documentazione è destinata principalmente a persone che non conoscono l'argomento. Pertanto, deve soddisfare diversi punti importanti: -- **Quando si scrive, iniziare con argomenti semplici e generali, per poi passare ad argomenti più avanzati alla fine. -- Fornire solo le informazioni che l'utente ha realmente bisogno di sapere sull'argomento. -- Verificate che le vostre informazioni siano effettivamente vere. Testate l'esempio prima di fornirlo. -- Siate concisi: tagliate a metà ciò che scrivete. E poi sentitevi liberi di rifarlo. -- Cercate di spiegare l'argomento nel modo migliore possibile. Ad esempio, provate prima a spiegare l'argomento a un collega. +- Iniziare con argomenti semplici e generali. Passare ad argomenti più avanzati alla fine +- Cercare di spiegare l'argomento nel modo più chiaro possibile. Ad esempio, provate prima a spiegare l'argomento a un collega. +- Fornite solo le informazioni che l'utente ha effettivamente bisogno di conoscere per un determinato argomento. +- Assicuratevi che le informazioni siano accurate. Testate ogni codice +- Siate concisi: tagliate a metà ciò che scrivete. E poi sentitevi liberi di rifarlo +- Usate con parsimonia l'evidenziazione, dai caratteri in grassetto ai riquadri, come ad esempio `.[note]` +- Seguire lo [standard di codifica |Coding Standard] nel codice -Tenete a mente questi punti durante tutto il processo di scrittura. La documentazione è scritta in [Texy! |https://texy.info], quindi imparate la sua [sintassi |syntax]. È possibile utilizzare l'editor di documentazione all'indirizzo https://editor.nette.org/ per visualizzare un'anteprima dell'articolo mentre lo si scrive. +Imparate anche la [sintassi |syntax]. Per avere un'anteprima dell'articolo durante la scrittura, si può usare l'[editor di anteprima |https://editor.nette.org/]. -Tra le regole generali di scrittura elencate in precedenza, si consiglia di attenersi alle seguenti: -- Il codice deve essere conforme allo [standard di codifica |Coding Standard]. -- Scrivere i nomi delle variabili, delle classi e dei metodi in inglese. -- Gli spazi dei nomi devono essere menzionati solo al primo colpo. -- Cercate di formattare il codice in modo da non visualizzare le barre di scorrimento. -- Risparmiate tutti i tipi di evidenziatori, dal grassetto alle `.[note]` caselle. -- Dalla documentazione, fare riferimento solo alla documentazione o a `www`. +Mutazioni linguistiche .[#toc-language-mutations] +------------------------------------------------- +L'inglese è la lingua principale, quindi le modifiche devono essere in inglese. Se l'inglese non è il vostro forte, usate [DeepL Translator |https://www.deepl.com/translator] e altri controlleranno il vostro testo. -Struttura della documentazione .[#toc-documentation-structure] --------------------------------------------------------------- +La traduzione in altre lingue avverrà automaticamente dopo l'approvazione e la messa a punto delle modifiche. + + +Modifiche banali .[#toc-trivial-edits] +-------------------------------------- + +Per contribuire alla documentazione, è necessario avere un account su [GitHub |https://github.com]. -La documentazione completa è ospitata su GitHub nel repository [nette/docs |https://github.com/nette/docs]. Questo repository è diviso in rami in base alla versione della documentazione, ad esempio il ramo `doc-3.1` contiene la documentazione per la versione 3.1. C'è poi il ramo `nette.org`, che contiene i contenuti degli altri sottodomini di nette.org. +Il modo più semplice per apportare piccole modifiche alla documentazione è utilizzare i link alla fine di ogni pagina: -Ogni ramo è poi suddiviso in diverse cartelle: +- *Mostra su GitHub* apre la versione sorgente della pagina su GitHub. Poi basta premere il pulsante `E` e si può iniziare a modificare (è necessario aver effettuato l'accesso a GitHub). +- *Apri anteprima* apre un editor in cui è possibile vedere immediatamente la forma visiva finale -* `cs` e `en`: contengono i file di documentazione per ogni versione linguistica -* `files`: immagini che possono essere incorporate nelle pagine di documentazione +Poiché l'[editor di anteprima |https://editor.nette.org/] non ha la possibilità di salvare le modifiche direttamente su GitHub, è necessario copiare il testo sorgente negli appunti (usando il pulsante *Copia negli appunti*) e poi incollarlo nell'editor su GitHub. +Sotto il campo di modifica c'è un modulo per l'invio. Qui, non dimenticate di riassumere brevemente e spiegare il motivo della vostra modifica. Dopo l'invio, viene creata una cosiddetta richiesta di pull (PR), che può essere ulteriormente modificata. -Il percorso di un file senza estensione corrisponde all'URL di una pagina della documentazione. Così, il file `en/quickstart/single-post.texy` avrà l'URL `doc.nette.org/en/quickstart/single-post`. +Modifiche più ampie .[#toc-larger-edits] +---------------------------------------- -Contribuire .[#toc-contributing] --------------------------------- +È più opportuno conoscere le basi del lavoro con il sistema di controllo di versione Git, piuttosto che affidarsi esclusivamente all'interfaccia di GitHub. Se non si ha familiarità con Git, si può fare riferimento alla [guida git - the simple |https://rogerdudler.github.io/git-guide/] e prendere in considerazione l'utilizzo di uno dei tanti [client grafici |https://git-scm.com/downloads/guis] disponibili. -Per contribuire alla documentazione, è necessario avere un account su [GitHub |https://github.com] e conoscere le basi di Git. Se non si ha familiarità con Git, si può consultare la guida rapida: [git - la guida semplice |https://rogerdudler.github.io/git-guide/], oppure utilizzare uno dei tanti strumenti grafici: [GIT - Client GUI |https://git-scm.com/downloads/guis]. +Modificare la documentazione nel modo seguente: -È possibile apportare semplici modifiche direttamente nell'interfaccia di GitHub. Tuttavia, è più comodo creare un fork del repository [nette/docs |https://github.com/nette/docs] e clonarlo sul proprio computer. Quindi apportare le modifiche nel ramo appropriato, eseguire il commit della modifica, eseguire il push sul proprio GitHub e inviare una richiesta di pull al repository `nette/docs` originale. +1) su GitHub, creare un [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] del repository [nette/docs |https://github.com/nette/docs] +2) [clonare |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] questo repository sul proprio computer +3) quindi, apportare le modifiche nel [ramo appropriato |#Documentation Structure] +4) verificare la presenza di spazi extra nel testo utilizzando lo strumento [Code-Checker |code-checker:] +5) salvare (commit) le modifiche +6) se si è soddisfatti delle modifiche, inviarle su GitHub al proprio fork +7) da lì, inviarle al repository `nette/docs` creando una [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +È frequente ricevere commenti con suggerimenti. Tenere traccia delle modifiche proposte e incorporarle. Aggiungere le modifiche suggerite come nuovi commit e inviarle nuovamente a GitHub. Non creare mai una nuova richiesta di pull solo per modificare una richiesta esistente. + + +Struttura della documentazione .[#toc-documentation-structure] +-------------------------------------------------------------- -Prima di ogni richiesta di pull, è una buona idea eseguire [Code-Checker |code-checker:] per controllare gli spazi bianchi extra nel testo. +L'intera documentazione si trova su GitHub nel repository [nette/docs |https://github.com/nette/docs]. La versione attuale si trova nel ramo master, mentre le versioni precedenti si trovano nei rami `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Il contenuto di ogni ramo è suddiviso in cartelle principali che rappresentano le singole aree della documentazione. Per esempio, `application/` corrisponde a https://doc.nette.org/en/application, `latte/` corrisponde a https://latte.nette.org, ecc. Ognuna di queste cartelle contiene sottocartelle che rappresentano le mutazioni linguistiche (`cs`, `en`, ...) e, opzionalmente, una sottocartella `files` con immagini che possono essere inserite nelle pagine della documentazione. diff --git a/contributing/pl/documentation.texy b/contributing/pl/documentation.texy index 219d2598dd..bc8e0c101a 100644 --- a/contributing/pl/documentation.texy +++ b/contributing/pl/documentation.texy @@ -1,55 +1,69 @@ -Pisanie dokumentacji +Wkład w dokumentację ******************** .[perex] -Wkład w dokumentację jest jednym z wielu sposobów, w jaki możesz pomóc Nette. Jest to również jedno z najbardziej satysfakcjonujących zajęć, ponieważ pomagasz innym zrozumieć ramy. +Wkład w dokumentację jest jednym z najbardziej wartościowych działań, ponieważ pomaga innym zrozumieć framework. Jak pisać? .[#toc-how-to-write] ------------------------------- -Dokumentacja przeznaczona jest głównie dla osób, które dopiero zapoznają się z tematem. Dlatego powinien on spełniać kilka ważnych punktów: +Dokumentacja jest przeznaczona przede wszystkim dla osób, które są nowe w temacie. Dlatego powinna spełniać kilka ważnych punktów: -- **Pisząc, zacznij od tego, co proste i ogólne, a na końcu przejdź do bardziej zaawansowanych tematów**. -- Dostarczaj tylko te informacje, które użytkownik naprawdę potrzebuje wiedzieć na dany temat. -- Zweryfikuj, czy Twoje informacje są rzeczywiście prawdziwe. Przetestuj najpierw przykład przed podaniem kodu. -- Bądź zwięzły - skróć to, co piszesz o połowę. A potem nie krępuj się zrobić tego ponownie. -- Postaraj się wyjaśnić sprawę najlepiej jak potrafisz. Na przykład spróbuj najpierw wyjaśnić temat koledze. +- Zacznij od prostych i ogólnych tematów. Przejdź do bardziej zaawansowanych tematów na końcu +- Staraj się wyjaśnić temat tak jasno, jak to możliwe. Na przykład, spróbuj najpierw wytłumaczyć temat koledze. +- Podawaj tylko te informacje, które użytkownik rzeczywiście musi znać dla danego tematu +- Upewnij się, że informacje są dokładne. Testuj każdy kod +- Bądź zwięzły - skróć to, co piszesz o połowę. A potem nie krępuj się zrobić tego ponownie +- Oszczędnie używaj wyróżnień, od pogrubionych czcionek po ramki typu `.[note]` +- Stosuj się do [Standardów Kodowania |Coding Standard] w kodzie -Pamiętajcie o tych punktach w trakcie procesu pisania. Więcej wskazówek znajdziesz w artykule [Pisanie dla |https://www.lupa.cz/clanky/piseme-pro-web/] sieci. Dokumentacja jest napisana w [Texy! |https://texy.info], więc naucz się jej [składni |syntax]. Możesz użyć edytora dokumentacji na stronie https://editor.nette.org/, aby wyświetlić podgląd artykułu w trakcie jego pisania. +Ucz się również [składni |syntax]. Aby uzyskać podgląd artykułu podczas pisania, możesz użyć [edytora |https://editor.nette.org/] podglądu. -Oprócz powyższych punktów prosimy również o przestrzeganie następujących wskazówek: -- Angielski jest językiem podstawowym, więc Twoje zmiany powinny być w obu językach. Jeśli język angielski nie jest Twoją mocną stroną, skorzystaj z [DeepL Translator |https://www.deepl.com/translator], a inni dokonają korekty Twojego tekstu. -- W tekście dokumentacji mamy tendencję do "machania" i bycia grzecznym. -- W przykładach postępuj zgodnie ze [Standardem Kodowania |Coding Standard]. -- Pisz nazwy zmiennych, klas i metod w języku angielskim. -- Przestrzenie nazw powinny być podawane przy pierwszej wzmiance. -- Spróbuj sformatować swój kod, aby paski przewijania nie pojawiały się. -- Daruj sobie wszelkiego rodzaju zakreślacze, od pogrubień po obramowania. `.[note]`. -- Z dokumentacji należy odnosić się tylko do dokumentacji lub `www`. +Mutacje językowe .[#toc-language-mutations] +------------------------------------------- +Angielski jest językiem podstawowym, więc twoje zmiany powinny być w języku angielskim. Jeśli angielski nie jest twoją mocną stroną, użyj [DeepL Translator |https://www.deepl.com/translator] i inni sprawdzą twój tekst. -Struktura dokumentacji .[#toc-documentation-structure] ------------------------------------------------------- +Tłumaczenie na inne języki zostanie wykonane automatycznie po zatwierdzeniu i dopracowaniu twojej edycji. + + +Trywialne edycje .[#toc-trivial-edits] +-------------------------------------- + +Aby wnieść swój wkład w dokumentację, musisz mieć konto na [GitHubie |https://github.com]. -Cała dokumentacja jest hostowana na GitHubie w repozytorium [nette/docs |https://github.com/nette/docs]. To repozytorium jest podzielone na gałęzie w zależności od wersji dokumentacji, na przykład gałąź `doc-3.1` zawiera dokumentację dla wersji 3.1. Następnie jest gałąź `nette.org`, która zawiera zawartość pozostałych subdomen nette.org. +Najłatwiejszym sposobem na dokonanie niewielkiej zmiany w dokumentacji jest użycie linków na końcu każdej strony: -Każdy oddział jest następnie podzielony na kilka folderów: +- *Show on GitHub* otwiera wersję źródłową strony na GitHubie. Następnie wystarczy nacisnąć przycisk `E` i można rozpocząć edycję (trzeba być zalogowanym na GitHubie) +- *Open preview* otwiera edytor, w którym można od razu zobaczyć ostateczną formę wizualną -* `cs` i `en`: zawiera pliki dokumentacji dla każdej wersji językowej. -* `files`: obrazy, które mogą być osadzone na stronach dokumentacji +Ponieważ [edytor |https://editor.nette.org/] podglądu nie ma możliwości zapisywania zmian bezpośrednio na GitHubie, należy skopiować tekst źródłowy do schowka (za pomocą przycisku *Copy to clipboard*), a następnie wkleić go do edytora na GitHubie. +Poniżej pola edycyjnego znajduje się formularz do przesłania. Tutaj nie zapomnij krótko podsumować i wyjaśnić powodu swojej edycji. Po przesłaniu tworzony jest tzw. pull request (PR), który można dalej edytować. -Ścieżka do pliku bez rozszerzenia odpowiada adresowi URL strony w dokumentacji. W ten sposób plik `cs/quickstart/single-post.texy` będzie miał adres `doc.nette.org/en/quickstart/single-post`. +Większe edycje .[#toc-larger-edits] +----------------------------------- -Wkład w dokumentację .[#toc-contributing] ------------------------------------------ +Bardziej właściwe jest zapoznanie się z podstawami pracy z systemem kontroli wersji Git, niż poleganie wyłącznie na interfejsie GitHub. Jeśli nie jesteś zaznajomiony z Gitem, możesz zapoznać się z [git - prostym przewodnikiem |https://rogerdudler.github.io/git-guide/] i rozważyć użycie jednego z wielu dostępnych [klientów graficznych |https://git-scm.com/downloads/guis]. -Aby wnieść wkład w dokumentację, musisz mieć konto na [GitHubie |https://github.com] i podstawowe zrozumienie systemu wersjonowania Git. Jeśli nie jesteś zaznajomiony z git, możesz sprawdzić ten szybki przewodnik: [git - prosty przewodnik |https://rogerdudler.github.io/git-guide/], lub użyć jednego z wielu graficznych narzędzi: [GIT - klienci GUI |https://git-scm.com/downloads/guis]. +Edytuj dokumentację w następujący sposób: -Możesz wprowadzić proste zmiany bezpośrednio w interfejsie GitHub. Wygodniej jest jednak stworzyć fork repozytorium [nette/docs |https://github.com/nette/docs] i sklonować go na swój komputer. Następnie wprowadź zmiany w odpowiedniej gałęzi, popełnij zmianę, popchnij do swojego repozytorium GitHub i wyślij pull request do oryginalnego repozytorium `nette/docs`. Zauważ, że głównym językiem dokumentacji jest angielski, więc dokonaj zmian w obu językach. +1) na GitHubie utwórz [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] repozytorium [nette/docs |https://github.com/nette/docs] +2) [sklonuj |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] to repozytorium na swój komputer +3) następnie wprowadź zmiany w [odpowiedniej gałęzi |#Documentation Structure] +4) sprawdzić, czy w tekście nie ma dodatkowych spacji za pomocą narzędzia [Code-Checker |code-checker:] +5) zapisz (commit) zmiany +6) jeśli jesteś zadowolony ze zmian, wepchnij je na GitHub do swojego forka +7) stamtąd prześlij je do repozytorium `nette/docs` tworząc [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Powszechne jest otrzymywanie komentarzy z sugestiami. Śledź proponowane zmiany i włączaj je. Dodaj sugerowane zmiany jako nowe commity i wyślij je ponownie do GitHub. Nigdy nie twórz nowego pull requesta tylko po to, by zmodyfikować istniejący. + + +Struktura dokumentacji .[#toc-documentation-structure] +------------------------------------------------------ -Przed każdym pull requestem warto uruchomić [Code-Checker |code-checker:], aby sprawdzić czy w tekście nie ma dodatkowych spacji. +Cała dokumentacja znajduje się na GitHubie w repozytorium [nette/docs |https://github.com/nette/docs]. Aktualna wersja znajduje się w gałęzi master, natomiast starsze wersje znajdują się w gałęziach takich jak `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Zawartość każdej gałęzi podzielona jest na główne foldery reprezentujące poszczególne obszary dokumentacji. Na przykład `application/` odpowiada https://doc.nette.org/en/application, `latte/` odpowiada https://latte.nette.org, itd. Każdy z tych folderów zawiera podfoldery reprezentujące mutacje językowe (`cs`, `en`, ...) oraz opcjonalnie podfolder `files` z obrazkami, które można wstawić na strony w dokumentacji. diff --git a/contributing/pt/documentation.texy b/contributing/pt/documentation.texy index de3552924c..8a1b434f8c 100644 --- a/contributing/pt/documentation.texy +++ b/contributing/pt/documentation.texy @@ -1,53 +1,69 @@ -Escrevendo a documentação -************************* +Contribuição para a documentação +******************************** .[perex] -Contribuir com a documentação é uma das muitas maneiras pelas quais você pode ajudar a Nette. É também uma das atividades mais gratificantes, pois você ajuda os outros a entender a estrutura. +Contribuir com a documentação é uma das atividades mais valiosas, pois ajuda os outros a entender a estrutura. Como Escrever? .[#toc-how-to-write] ----------------------------------- -A documentação é destinada principalmente às pessoas que estão apenas se familiarizando com o tema. Portanto, ela deve atender a vários pontos importantes: +A documentação é destinada principalmente às pessoas que são novatas no assunto. Portanto, ela deve atender a vários pontos importantes: -- ** Ao escrever, comece com o simples e geral, e passe para tópicos mais avançados no final.** -- Fornecer apenas as informações que o usuário realmente precisa saber sobre o tópico. -- Verifique se suas informações são realmente verdadeiras. Teste o exemplo primeiro antes de dar o exemplo. -- Seja conciso - corte o que você escreve pela metade. E depois sinta-se à vontade para fazer isso novamente. -- Tente explicar o assunto da melhor forma possível. Por exemplo, tente explicar o assunto a um colega primeiro. +- Comece com temas simples e gerais. Passar para tópicos mais avançados no final +- Tente explicar o tópico da maneira mais clara possível. Por exemplo, tente explicar o tópico a um colega primeiro +- Fornecer apenas informações que o usuário realmente precisa saber para um determinado tópico +- Certifique-se de que suas informações sejam precisas. Teste cada código +- Seja conciso - corte o que você escreve pela metade. E depois sinta-se livre para fazê-lo novamente +- Use o destaque com moderação, desde fontes ousadas até molduras como `.[note]` +- Siga a [Norma de Codificação |Coding Standard] no código -Mantenha estes pontos em mente durante todo o processo de escrita. A documentação é escrita em [Texy! |https://texy.info], portanto, aprenda sua [sintaxe |syntax]. Você pode usar o editor da documentação em https://editor.nette.org/ para visualizar o artigo enquanto o escreve. +Além disso, aprenda a [sintaxe |syntax]. Para uma prévia do artigo durante a redação, você pode usar o [editor de prévia |https://editor.nette.org/]. -Entre as regras gerais de redação listadas anteriormente, por favor, mantenha as seguintes: -- Seu código deve estar em conformidade com a [Norma de Codificação |Coding Standard]. -- Escreva os nomes das variáveis, classes e métodos em inglês. -- Namespaces só precisam ser mencionados na primeira menção. -- Tente formatar o código de modo que as barras de rolagem não sejam exibidas. -- Poupe todos os tipos de marcadores, desde boldface até `.[note]` caixas. -- A partir da documentação, consulte apenas a documentação ou `www`. +Mutações de idiomas .[#toc-language-mutations] +---------------------------------------------- +O inglês é o idioma principal, portanto, suas mudanças devem ser em inglês. Se o inglês não for o seu forte, use [DeepL Tradutor |https://www.deepl.com/translator] e outros verificarão seu texto. -Estrutura de documentação .[#toc-documentation-structure] ---------------------------------------------------------- +A tradução para outros idiomas será feita automaticamente após a aprovação e o ajuste fino de sua edição. + + +Edições triviais .[#toc-trivial-edits] +-------------------------------------- + +Para contribuir com a documentação, você precisa ter uma conta no [GitHub |https://github.com]. -A documentação completa está hospedada no GitHub, no repositório [nette/docs |https://github.com/nette/docs]. Este repositório é dividido em filiais com base na versão da documentação, por exemplo, a filial `doc-3.1` contém a documentação da versão 3.1. E depois há a filial `nette.org`, que contém o conteúdo dos outros subdomínios da nette.org. +A maneira mais fácil de fazer uma pequena mudança na documentação é usar os links no final de cada página: -Cada ramo é então dividido em várias pastas: +- *Show on GitHub* abre a versão de origem da página no GitHub. Depois basta pressionar o botão `E` e você pode começar a editar (você deve estar logado no GitHub) +- *Abrir visualização* abre um editor onde você pode ver imediatamente a forma visual final -* `cs` e `en`: contém arquivos de documentação para cada versão linguística -* `files`: imagens que podem ser incorporadas nas páginas de documentação +Como o [editor de visualização |https://editor.nette.org/] não tem a capacidade de salvar as mudanças diretamente no GitHub, você precisa copiar o texto fonte para a área de transferência (usando o botão *Copy to clipboard*) e depois colá-lo no editor no GitHub. +Abaixo do campo de edição, há um formulário para submissão. Aqui, não se esqueça de resumir e explicar brevemente o motivo de sua edição. Após a submissão, é criado um chamado pull request (PR), que pode ser editado posteriormente. -O caminho para um arquivo sem extensão corresponde ao URL de uma página na documentação. Assim, o arquivo `en/quickstart/single-post.texy` terá a URL `doc.nette.org/en/quickstart/single-post`. +Maiores edições .[#toc-larger-edits] +------------------------------------ -Contribuindo .[#toc-contributing] ---------------------------------- +É mais apropriado estar familiarizado com o básico de trabalhar com o sistema de controle de versões Git, em vez de depender apenas da interface GitHub. Se você não estiver familiarizado com Git, você pode se referir ao [git - o guia simples |https://rogerdudler.github.io/git-guide/] e considerar o uso de um dos muitos [clientes gráficos |https://git-scm.com/downloads/guis] disponíveis. -Para contribuir com a documentação, você deve ter uma conta no [GitHub |https://github.com] e conhecer o básico de [GitHub |https://github.com]. Se você não está familiarizado com Git, pode verificar o guia rápido: [git - o guia simples |https://rogerdudler.github.io/git-guide/], ou usar uma das muitas ferramentas gráficas: [GIT - clientes da GUI |https://git-scm.com/downloads/guis]. +Edite a documentação da seguinte maneira: -Você pode fazer mudanças simples diretamente na interface do GitHub. Entretanto, é mais conveniente criar um garfo do repositório [nette/docs |https://github.com/nette/docs] e cloná-lo em seu computador. Em seguida, faça mudanças no ramo apropriado, comprometa a mudança, empurre para seu GitHub e envie um pedido de puxar para o repositório original `nette/docs`. +1) no GitHub, criar um [garfo |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] do repositório [nette/docs |https://github.com/nette/docs] +2) [clone |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] este repositório em seu computador +3) então, fazer mudanças no [ramo apropriado |#Documentation Structure] +4) verificar se há espaços extras no texto usando a ferramenta [Code-Checker |code-checker:] +5) salvar (comprometer) as mudanças +6) se você estiver satisfeito com as mudanças, empurre-as para GitHub até sua bifurcação +7) a partir daí, submetê-los ao repositório `nette/docs`, criando um [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +É comum receber comentários com sugestões. Mantenha-se informado sobre as mudanças propostas e incorpore-as. Adicione as mudanças sugeridas como novos compromissos e reenvie-as ao GitHub. Nunca crie um novo pedido de puxar só para modificar um já existente. + + +Estrutura de documentação .[#toc-documentation-structure] +--------------------------------------------------------- -Antes de cada pedido de puxar, é uma boa idéia executar o [Code-Checker |code-checker:] para verificar o espaço extra em branco no texto. +Toda a documentação está localizada no GitHub, no repositório [nette/docs |https://github.com/nette/docs]. A versão atual está na filial principal, enquanto as versões mais antigas estão localizadas em filiais como `doc-3.x`, `doc-2.x`. -{{priority: -1}} +O conteúdo de cada ramo é dividido em pastas principais que representam áreas individuais de documentação. Por exemplo, `application/` corresponde a https://doc.nette.org/en/application, `latte/` corresponde a https://latte.nette.org, etc. Cada uma destas pastas contém subpastas representando as mutações lingüísticas (`cs`, `en`, ...) e opcionalmente uma subpasta `files` com imagens que podem ser inseridas nas páginas da documentação. diff --git a/contributing/ro/documentation.texy b/contributing/ro/documentation.texy index 3d6cdf83dc..78df00669c 100644 --- a/contributing/ro/documentation.texy +++ b/contributing/ro/documentation.texy @@ -1,53 +1,69 @@ -Scrierea documentației -********************** +Contribuția la documentație +*************************** .[perex] -Contribuția la documentație este unul dintre multele moduri în care îl puteți ajuta pe Nette. Este, de asemenea, una dintre cele mai satisfăcătoare activități, deoarece îi ajutați pe alții să înțeleagă cadrul. +Contribuția la documentație este una dintre cele mai valoroase activități, deoarece îi ajută pe ceilalți să înțeleagă cadrul. -Cum să scrieți? .[#toc-how-to-write] ------------------------------------- +Cum se scrie? .[#toc-how-to-write] +---------------------------------- -Documentația este destinată în primul rând persoanelor care abia se familiarizează cu acest subiect. Prin urmare, ar trebui să îndeplinească câteva puncte importante: +Documentația este destinată în primul rând persoanelor care nu cunosc acest subiect. Prin urmare, ar trebui să îndeplinească mai multe puncte importante: -- **Când scrieți, începeți cu lucruri simple și generale, iar la sfârșit treceți la subiecte mai avansate.** -- Furnizați numai informațiile pe care utilizatorul trebuie să le știe cu adevărat despre subiect. -- Verificați dacă informațiile pe care le oferiți sunt de fapt adevărate. Testați mai întâi exemplul înainte de a da exemplul. -- Fiți concis - reduceți la jumătate ceea ce scrieți. Și apoi nu ezitați să o faceți din nou. -- Încercați să explicați cât mai bine subiectul. De exemplu, încercați să explicați mai întâi subiectul unui coleg. +- Începeți cu subiecte simple și generale. Treceți la subiecte mai avansate la sfârșit +- Încercați să explicați subiectul cât mai clar posibil. De exemplu, încercați să explicați mai întâi subiectul unui coleg +- Furnizați numai informațiile pe care utilizatorul trebuie să le știe cu adevărat pentru un anumit subiect +- Asigurați-vă că informațiile pe care le furnizați sunt corecte. Testați fiecare cod +- Fiți concis - reduceți la jumătate ceea ce scrieți. Și apoi nu ezitați să o faceți din nou +- Folosiți evidențierea cu moderație, de la fonturi îngroșate la cadre de genul `.[note]` +- Respectați [standardul de codi |Coding Standard] ficare în cod -Țineți minte aceste puncte pe tot parcursul procesului de scriere. Documentația este scrisă în [Texy!" |https://texy.info], așa că învățați [sintaxa |syntax] acesteia. Puteți utiliza editorul de documentație de la https://editor.nette.org/ pentru a previzualiza articolul pe măsură ce îl scrieți. +De asemenea, învățați [sintaxa |syntax]. Pentru o previzualizare a articolului în timpul scrierii, puteți utiliza [editorul de previzualizare |https://editor.nette.org/]. -Printre regulile generale de scriere enumerate anterior, respectați următoarele: -- Codul dumneavoastră trebuie să fie în conformitate cu [Standardul de codare |Coding Standard]. -- Scrieți numele variabilelor, claselor și metodelor în limba engleză. -- Spațiile de nume trebuie să fie menționate doar la prima mențiune. -- Încercați să formatați codul astfel încât să nu fie afișate bare de defilare. -- Scăpați de toate tipurile de evidențiere, de la boldface la `.[note]` casete. -- Din documentație, faceți trimitere doar la documentație sau la `www`. +Mutații lingvistice .[#toc-language-mutations] +---------------------------------------------- +Engleza este limba principală, așa că modificările trebuie să fie în limba engleză. Dacă engleza nu este punctul dumneavoastră forte, utilizați [DeepL Translator |https://www.deepl.com/translator] și alții vă vor verifica textul. -Structura documentației .[#toc-documentation-structure] -------------------------------------------------------- +Traducerea în alte limbi se va face automat după aprobarea și punerea la punct a modificării dumneavoastră. + + +Modificări triviale .[#toc-trivial-edits] +----------------------------------------- + +Pentru a contribui la documentație, trebuie să aveți un cont pe [GitHub |https://github.com]. -Documentația completă este găzduită pe GitHub, în depozitul [nette/docs |https://github.com/nette/docs]. Acest depozit este împărțit în ramuri în funcție de versiunea documentației, de exemplu, ramura `doc-3.1` conține documentația pentru versiunea 3.1. Și apoi există ramura `nette.org`, care conține conținutul celorlalte subdomenii ale nette.org. +Cel mai simplu mod de a face o mică modificare în documentație este să folosiți linkurile de la sfârșitul fiecărei pagini: -Fiecare ramură este apoi împărțită în mai multe dosare: +- *Show on GitHub* deschide versiunea sursă a paginii pe GitHub. Apoi, trebuie doar să apăsați butonul `E` și puteți începe să editați (trebuie să fiți logat pe GitHub) +- *Open preview* deschide un editor în care puteți vedea imediat forma vizuală finală -* `cs` și `en`: conține fișiere de documentație pentru fiecare versiune de limbă. -* `files`: imagini care pot fi încorporate în paginile de documentație +Deoarece [editorul de previzualizare |https://editor.nette.org/] nu are posibilitatea de a salva modificările direct pe GitHub, trebuie să copiați textul sursă în clipboard (folosind butonul *Copy to clipboard*) și apoi să îl lipiți în editorul de pe GitHub. +Sub câmpul de editare se află un formular pentru trimitere. Aici, nu uitați să rezumați pe scurt și să explicați motivul modificării dvs. După trimitere, se creează o așa-numită pull request (PR), care poate fi editată în continuare. -Calea către un fișier fără extensie corespunde URL-ului unei pagini din documentație. Astfel, fișierul `en/quickstart/single-post.texy` va avea URL-ul `doc.nette.org/en/quickstart/single-post`. +Modificări mai mari .[#toc-larger-edits] +---------------------------------------- -Contribuția .[#toc-contributing] --------------------------------- +Este mai indicat să vă familiarizați cu elementele de bază ale lucrului cu sistemul de control al versiunilor Git decât să vă bazați exclusiv pe interfața GitHub. Dacă nu sunteți familiarizat cu Git, puteți consulta [ghidul git - the simple guide |https://rogerdudler.github.io/git-guide/] și puteți lua în considerare utilizarea unuia dintre numeroșii [clienți grafici |https://git-scm.com/downloads/guis] disponibili. -Pentru a contribui la documentație, trebuie să aveți un cont pe [GitHub |https://github.com] și să cunoașteți elementele de bază ale Git. Dacă nu sunteți familiarizat cu Git, puteți consulta ghidul rapid: [git - ghidul simplu |https://rogerdudler.github.io/git-guide/] sau puteți utiliza unul dintre numeroasele instrumente grafice: [GIT - clienți GUI |https://git-scm.com/downloads/guis]. +Modificați documentația în felul următor: -Puteți face modificări simple direct în interfața GitHub. Cu toate acestea, este mai convenabil să creați o bifurcație a depozitului [nette/docs |https://github.com/nette/docs] și să o clonați pe computerul dumneavoastră. Apoi, efectuați modificări în ramura corespunzătoare, confirmați modificarea, împingeți-o în GitHub-ul dvs. și trimiteți un pull request la depozitul original `nette/docs`. +1) pe GitHub, creați o [bifurcație |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] a depozitului [nette/docs |https://github.com/nette/docs] +2) [clonați |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] acest depozit pe computerul dvs. +3) apoi, efectuați modificări în [ramura corespunzătoare |#Documentation Structure] +4) verificați dacă există spații în plus în text cu ajutorul instrumentului [Code-Checker |code-checker:] +5) salvați (confirmați) modificările +6) dacă sunteți mulțumit de modificări, împingeți-le pe GitHub în bifurcația dvs. +7) de acolo, trimiteți-le la depozitul `nette/docs` creând o [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Este obișnuit să primiți comentarii cu sugestii. Țineți evidența modificărilor propuse și încorporați-le. Adăugați modificările sugerate ca noi comisioane și retrimiteți-le la GitHub. Nu creați niciodată un nou pull request doar pentru a modifica unul existent. + + +Structura documentației .[#toc-documentation-structure] +------------------------------------------------------- -Înainte de fiecare pull request, este o idee bună să rulați [Code-Checker |code-checker:] pentru a verifica spațiile albe suplimentare din text. +Întreaga documentație se găsește pe GitHub, în depozitul [nette/docs |https://github.com/nette/docs]. Versiunea curentă se află în ramura master, în timp ce versiunile mai vechi se află în ramuri precum `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Conținutul fiecărei ramuri este împărțit în dosare principale care reprezintă domenii individuale ale documentației. De exemplu, `application/` corespunde la https://doc.nette.org/en/application, `latte/` corespunde la https://latte.nette.org, etc. Fiecare dintre aceste dosare conține subdosare care reprezintă mutații lingvistice (`cs`, `en`, ...) și, opțional, un subdosar `files` cu imagini care pot fi inserate în paginile din documentație. diff --git a/contributing/ru/documentation.texy b/contributing/ru/documentation.texy index bf8c104aca..0cd5eaf805 100644 --- a/contributing/ru/documentation.texy +++ b/contributing/ru/documentation.texy @@ -1,53 +1,69 @@ -Написание документации -********************** +Вклад в документацию +******************** .[perex] -Вклад в документацию - это один из многих способов помочь Nette. Это также один из самых полезных видов деятельности, поскольку вы помогаете другим понять фреймворк. +Вклад в документацию является одним из наиболее ценных видов деятельности, поскольку он помогает другим понять концепцию. Как писать? .[#toc-how-to-write] -------------------------------- -Документация предназначена в первую очередь для людей, которые только знакомятся с темой. Поэтому она должна отвечать нескольким важным требованиям: +Документация в первую очередь предназначена для людей, которые впервые сталкиваются с этой темой. Поэтому она должна отвечать нескольким важным требованиям: -- **При написании документации начинайте с простого и общего, а к более сложным темам переходите в конце**. -- Предоставляйте только ту информацию, которую пользователю действительно необходимо знать о теме. -- Убедитесь, что ваша информация действительно правдива. Прежде чем привести пример, сначала проверьте его на практике. -- Будьте лаконичны - сократите то, что вы пишете, вдвое. А затем не стесняйтесь сделать это еще раз. -- Постарайтесь объяснить суть вопроса как можно лучше. Например, попробуйте сначала объяснить тему коллеге. +- Начинайте с простых и общих тем. В конце переходите к более сложным темам. +- Старайтесь объяснять тему как можно понятнее. Например, попробуйте сначала объяснить тему коллеге. +- Предоставляйте только ту информацию, которую пользователю действительно необходимо знать по данной теме. +- Убедитесь, что ваша информация точна. Тестируйте каждый код +- Будьте лаконичны - сократите то, что вы пишете, вдвое. А затем не стесняйтесь сделать это снова. +- Используйте выделение экономно, от жирного шрифта до рамок типа `.[note]` +- Следуйте [стандарту кодирования |Coding Standard] в коде -Помните об этих моментах на протяжении всего процесса написания. Документация написана на языке [Texy! |https://texy.info], поэтому изучите его [синтаксис |syntax]. Вы можете использовать редактор документации на сайте https://editor.nette.org/ для предварительного просмотра статьи в процессе ее написания. +Также изучите [синтаксис |syntax]. Для предварительного просмотра статьи во время написания вы можете использовать [редактор предварительного просмотра |https://editor.nette.org/]. -Помимо общих правил написания, перечисленных ранее, придерживайтесь следующих: -- Ваш код должен соответствовать [Стандарту кодирования |Coding Standard]. -- Пишите имена переменных, классов и методов на английском языке. -- Пространства имен должны упоминаться только при первом упоминании. -- Старайтесь форматировать код так, чтобы не отображались полосы прокрутки. -- Избавьтесь от всех видов выделения, от жирного шрифта до `.[note]` квадратиков. -- Из документации ссылайтесь только на документацию или `www`. +Мутации языка .[#toc-language-mutations] +---------------------------------------- +Английский является основным языком, поэтому ваши изменения должны быть на английском. Если английский не является вашей сильной стороной, используйте [DeepL Переводчик |https://www.deepl.com/translator], и другие проверят ваш текст. -Структура документации .[#toc-documentation-structure] ------------------------------------------------------- +Перевод на другие языки будет выполнен автоматически после одобрения и доработки вашей правки. + + +Тривиальные правки .[#toc-trivial-edits] +---------------------------------------- + +Чтобы внести свой вклад в документацию, вам необходимо иметь учетную запись на [GitHub |https://github.com]. -Полная документация размещена на GitHub в репозитории [nette/docs |https://github.com/nette/docs]. Этот репозиторий разделен на ветви в зависимости от версии документации, например, ветвь `doc-3.1` содержит документацию для версии 3.1. Кроме того, есть ветка `nette.org`, которая содержит содержимое других поддоменов nette.org. +Самый простой способ внести небольшие изменения в документацию - воспользоваться ссылками в конце каждой страницы: -Затем каждая ветка делится на несколько папок: +- *Показать на GitHub* открывает исходную версию страницы на GitHub. Затем просто нажмите кнопку `E`, и вы сможете начать редактирование (вы должны быть зарегистрированы на GitHub). +- *Открыть предварительный просмотр* открывает редактор, где вы можете сразу увидеть окончательный визуальный вид -* `cs` и `en`: содержит файлы документации для каждой языковой версии. -* `files`: изображения, которые могут быть вставлены в страницы документации +Поскольку [редактор предварительного просмотра |https://editor.nette.org/] не имеет возможности сохранять изменения непосредственно на GitHub, вам необходимо скопировать исходный текст в буфер обмена (с помощью кнопки *Копировать в буфер обмена*), а затем вставить его в редактор на GitHub. +Ниже поля редактирования находится форма для отправки. Здесь не забудьте кратко изложить и объяснить причину вашей правки. После отправки создается так называемый pull request (PR), который можно в дальнейшем редактировать. -Путь к файлу без расширения соответствует URL страницы в документации. Таким образом, файл `en/quickstart/single-post.texy` будет иметь URL `doc.nette.org/en/quickstart/single-post`. +Более крупные правки .[#toc-larger-edits] +----------------------------------------- -Внесение вклада .[#toc-contributing] ------------------------------------- +Целесообразнее ознакомиться с основами работы с системой контроля версий Git, чем полагаться только на интерфейс GitHub. Если вы не знакомы с Git, вы можете обратиться к [git - простому руководству |https://rogerdudler.github.io/git-guide/] и рассмотреть возможность использования одного из множества доступных [графических клиентов |https://git-scm.com/downloads/guis]. -Чтобы внести свой вклад в документацию, вы должны иметь учетную запись на [GitHub |https://github.com] и знать основы Git. Если вы не знакомы с Git'ом, вы можете ознакомиться с кратким руководством: [git - the simple guide |https://rogerdudler.github.io/git-guide/], или воспользоваться одним из многочисленных графических инструментов: [GIT - GUI-клиенты |https://git-scm.com/downloads/guis]. +Отредактируйте документацию следующим образом: -Вы можете вносить простые изменения непосредственно в интерфейсе GitHub. Однако удобнее создать форк репозитория [nette/docs |https://github.com/nette/docs] и клонировать его на свой компьютер. Затем внесите изменения в соответствующую ветку, зафиксируйте изменения, переместите их на свой GitHub и отправьте запрос pull request в исходный репозиторий `nette/docs`. +1) на GitHub создайте [форк |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] репозитория [nette/docs |https://github.com/nette/docs] +2) [клонируйте |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] этот репозиторий на свой компьютер +3) затем внесите изменения в [соответствующую ветку |#Documentation Structure] +4) проверьте наличие лишних пробелов в тексте с помощью инструмента [Code-Checker |code-checker:] +5) сохраните (зафиксируйте) изменения +6) если вы удовлетворены изменениями, отправьте их на GitHub в свой форк +7) оттуда отправьте их в репозиторий `nette/docs`, создав [pull request|https://help.github.com/articles/creating-a-pull-request] исправление (PR). + +Обычно вы получаете комментарии с предложениями. Отслеживайте предложенные изменения и учитывайте их. Добавьте предложенные изменения как новые коммиты и повторно отправьте их в GitHub. Никогда не создавайте новый pull request только для того, чтобы изменить существующий. + + +Структура документации .[#toc-documentation-structure] +------------------------------------------------------ -Перед каждым запросом на исправление рекомендуется запустить [Code-Checker |code-checker:] для проверки лишних пробелов в тексте. +Вся документация находится на GitHub в репозитории [nette/docs |https://github.com/nette/docs]. Текущая версия находится в ветке master, а старые версии расположены в ветках `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Содержимое каждой ветки разделено на основные папки, представляющие отдельные области документации. Например, `application/` соответствует https://doc.nette.org/en/application, `latte/` соответствует https://latte.nette.org, и т.д. Каждая из этих папок содержит вложенные папки, представляющие языковые мутации (`cs`, `en`, ...) и, по желанию, вложенную папку `files` с изображениями, которые могут быть вставлены в страницы документации. diff --git a/contributing/sl/documentation.texy b/contributing/sl/documentation.texy index 6e671cabf7..261c76bd9a 100644 --- a/contributing/sl/documentation.texy +++ b/contributing/sl/documentation.texy @@ -1,53 +1,69 @@ -Pisanje dokumentacije -********************* +Prispevek k dokumentaciji +************************* .[perex] -Prispevek k dokumentaciji je eden od številnih načinov, kako lahko pomagate Nette. To je tudi ena najbolj koristnih dejavnosti, saj pomagate drugim razumeti ogrodje. +Prispevek k dokumentaciji je ena izmed najbolj dragocenih dejavnosti, saj drugim pomaga razumeti ogrodje. Kako pisati? .[#toc-how-to-write] --------------------------------- -Dokumentacija je v prvi vrsti namenjena ljudem, ki se šele spoznavajo s to temo. Zato mora izpolnjevati več pomembnih točk: +Dokumentacija je v prvi vrsti namenjena ljudem, ki se s tem področjem šele spoznavajo. Zato mora izpolnjevati več pomembnih točk: -- **Pri pisanju začnite s preprostimi in splošnimi temami, na koncu pa preidite na naprednejše teme.** -- Navedite le tiste informacije, ki jih uporabnik resnično potrebuje o temi. -- Preverite, ali so vaše informacije dejansko resnične. Najprej preizkusite primer, preden ga navedete. -- Bodite jedrnati - zapisano skrajšajte na polovico. In potem to lahko še enkrat ponovite. -- Poskusite zadevo čim bolje razložiti. Najprej poskusite na primer razložiti temo svojemu sodelavcu. +- Začnite s preprostimi in splošnimi temami. Na koncu preidite na naprednejše teme +- Poskusite temo razložiti čim bolj jasno. Na primer, poskusite temo najprej razložiti sodelavcu. +- Navedite le informacije, ki jih uporabnik dejansko potrebuje za določeno temo +- Prepričajte se, da so vaše informacije točne. Preizkusite vsako kodo +- Bodite jedrnati - napišite manj kot polovico. In potem to še enkrat ponovite. +- Varčno uporabljajte poudarke, od krepkih pisav do okvirjev, kot so `.[note]` +- V kodi upoštevajte [standard kodiranja |Coding Standard] -Te točke imejte v mislih ves čas pisanja. Dokumentacija je napisana v jeziku [Texy! |https://texy.info], zato se naučite njegove [sintakse |syntax]. Uporabite lahko urejevalnik dokumentacije na spletnem mestu https://editor.nette.org/ in si članek med pisanjem predhodno ogledate. +Naučite se tudi [sintakse |syntax]. Za predogled članka med pisanjem lahko uporabite [urejevalnik za predogled |https://editor.nette.org/]. -Med splošnimi pravili za pisanje, ki so bila navedena prej, se držite tudi naslednjih: -- Vaša koda mora biti v skladu s [standardom kodiranja |Coding Standard]. -- Imena spremenljivk, razredov in metod zapišite v angleščini. -- Imenski prostori morajo biti omenjeni le ob prvi omembi. -- Poskusite oblikovati kodo tako, da se ne bodo prikazovale vrstice za pomikanje. -- Prihranite vse vrste označevalnikov, od krepkega tiska do `.[note]` okvirjev. -- Iz dokumentacije se sklicujte samo na dokumentacijo ali na spletno stran `www`. +Jezikovne mutacije .[#toc-language-mutations] +--------------------------------------------- +Osnovni jezik je angleščina, zato morajo biti vaše spremembe v angleščini. Če angleščina ni vaša močna stran, uporabite [prevajalnik DeepL |https://www.deepl.com/translator] in drugi bodo preverili vaše besedilo. -Struktura dokumentacije .[#toc-documentation-structure] -------------------------------------------------------- +Prevod v druge jezike bo opravljen samodejno po odobritvi in dodelavi vašega urejanja. + + +Trivialna urejanja .[#toc-trivial-edits] +---------------------------------------- + +Če želite prispevati k dokumentaciji, morate imeti račun na [GitHubu |https://github.com]. -Celotna dokumentacija je objavljena na GitHubu v skladišču [nette/docs |https://github.com/nette/docs]. Ta skladišče je razdeljeno na veje glede na različico dokumentacije, na primer veja `doc-3.1` vsebuje dokumentacijo za različico 3.1. Potem pa je tu še veja `nette.org`, ki vsebuje vsebino drugih poddomen spletnega mesta nette.org. +Najlažji način za majhne spremembe v dokumentaciji je uporaba povezav na koncu vsake strani: -Vsaka veja je nato razdeljena na več map: +- *Pokaži na GitHubu* odpre izvorno različico strani na GitHubu. Nato samo pritisnite gumb `E` in lahko začnete urejati (prijavljeni morate biti v GitHub). +- *Open preview* odpre urejevalnik, v katerem si lahko takoj ogledate končno vizualno obliko -* `cs` in `en`: vsebujeta dokumentacijske datoteke za vsako jezikovno različico -* `files`: slike, ki jih je mogoče vgraditi v dokumentacijske strani +Ker [urejevalnik predogleda |https://editor.nette.org/] nima možnosti neposrednega shranjevanja sprememb v GitHub, morate izvorno besedilo kopirati v odložišče (z gumbom *Kopiraj v odložišče*) in ga nato prilepiti v urejevalnik na GitHubu. +Pod poljem za urejanje je obrazec za oddajo. Tu ne pozabite na kratko povzeti in pojasniti razloga za svoje urejanje. Po oddaji se ustvari tako imenovana zahteva za povleko (pull request, PR), ki jo lahko še naprej urejate. -Pot do datoteke brez končnice ustreza naslovu URL strani v dokumentaciji. Tako bo datoteka `en/quickstart/single-post.texy` imela URL `doc.nette.org/en/quickstart/single-post`. +Večja urejanja .[#toc-larger-edits] +----------------------------------- -Prispevek .[#toc-contributing] ------------------------------- +Primerneje je poznati osnove dela s sistemom za nadzor različic Git in se ne zanašati samo na vmesnik GitHub. Če sistema Git ne poznate, si lahko ogledate [priročnik git - the simple guide |https://rogerdudler.github.io/git-guide/] in razmislite o uporabi enega od številnih [grafičnih odjemalcev |https://git-scm.com/downloads/guis], ki so na voljo. -Če želite prispevati k dokumentaciji, morate imeti račun na [GitHubu |https://github.com] in poznati osnove Gita. Če sistema Git ne poznate, si lahko ogledate kratek vodnik: [git - preprost vodnik |https://rogerdudler.github.io/git-guide/] ali pa uporabite eno od številnih grafičnih orodij: [GIT - odjemalci grafičnega vmesnika |https://git-scm.com/downloads/guis]. +Dokumentacijo uredite na naslednji način: -Preproste spremembe lahko naredite neposredno v vmesniku GitHub. Vendar je bolj priročno, če ustvarite vilico skladišča [nette/docs |https://github.com/nette/docs] in ga klonirate v svoj računalnik. Nato naredite spremembe v ustrezni veji, potrdite spremembe, jih potisnite v svoj repozitorij GitHub in pošljite zahtevek za poteg v prvotni repozitorij `nette/docs`. +1) na spletnem mestu GitHub ustvarite [vilico |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] skladišča [nette/docs |https://github.com/nette/docs] +2) to skladišče [klonirajte |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] v svoj računalnik +3) nato vnesite spremembe v [ustrezno vejo |#Documentation Structure] +4) z orodjem [Code-Checker |code-checker:] preverite, ali so v besedilu dodatni presledki +5) shranite (commit) spremembe +6) če ste s spremembami zadovoljni, jih potisnite v GitHub v svojo vilico +7) od tam jih pošljite v repozitorij `nette/docs` tako, da ustvarite [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Pogosto se zgodi, da prejmete komentarje s predlogi. Spremljajte predlagane spremembe in jih vključite. Predlagane spremembe dodajte kot nove spremembe in jih ponovno pošljite v GitHub. Nikoli ne ustvarjajte nove zahteve za prenos samo zato, da bi spremenili obstoječo zahtevo. + + +Struktura dokumentacije .[#toc-documentation-structure] +------------------------------------------------------- -Pred vsako zahtevo za povlečenje je dobro zagnati program [Code-Checker |code-checker:], da preverite dodatne bele lise v besedilu. +Celotna dokumentacija se nahaja na GitHubu v skladišču [nette/docs |https://github.com/nette/docs]. Trenutna različica je v glavni veji, starejše različice pa se nahajajo v vejah, kot so `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Vsebina vsake veje je razdeljena na glavne mape, ki predstavljajo posamezna področja dokumentacije. Na primer, `application/` ustreza https://doc.nette.org/en/application, `latte/` ustreza https://latte.nette.org itd. Vsaka od teh map vsebuje podmape, ki predstavljajo jezikovne mutacije (`cs`, `en`, ...), in po želji podmapo `files` s slikami, ki jih je mogoče vstaviti na strani v dokumentaciji. diff --git a/contributing/tr/documentation.texy b/contributing/tr/documentation.texy index 0560b84589..cad1144972 100644 --- a/contributing/tr/documentation.texy +++ b/contributing/tr/documentation.texy @@ -1,53 +1,69 @@ -Dokümantasyonun Yazılması -************************* +Dokümantasyona Katkıda Bulunma +****************************** .[perex] -Belgelere katkıda bulunmak, Nette'e yardımcı olabileceğiniz birçok yoldan biridir. Ayrıca, başkalarının çerçeveyi anlamasına yardımcı olduğunuz için en ödüllendirici faaliyetlerden biridir. +Dokümantasyona katkıda bulunmak, başkalarının çerçeveyi anlamasına yardımcı olduğu için en değerli faaliyetlerden biridir. Nasıl Yazılır? .[#toc-how-to-write] ----------------------------------- -Dokümantasyon öncelikle konuyla yeni tanışan kişilere yöneliktir. Bu nedenle, birkaç önemli noktayı karşılamalıdır: +Dokümantasyon öncelikle konuya yeni başlayan kişilere yöneliktir. Bu nedenle, birkaç önemli noktayı karşılamalıdır: -- Yazarken, basit ve genel konularla başlayın ve sonunda daha gelişmiş konulara geçin.** -- Yalnızca kullanıcının konu hakkında gerçekten bilmesi gereken bilgileri sağlayın. -- Bilgilerinizin gerçekten doğru olduğunu doğrulayın. Örnek vermeden önce örneği test edin. -- Kısa ve öz olun - yazdıklarınızı ikiye bölün. Ve sonra tekrar yapmaktan çekinmeyin. -- Konuyu mümkün olduğunca iyi açıklamaya çalışın. Örneğin, konuyu önce bir iş arkadaşınıza anlatmayı deneyin. +- Basit ve genel konularla başlayın. Sonunda daha gelişmiş konulara geçin +- Konuyu mümkün olduğunca açık bir şekilde anlatmaya çalışın. Örneğin, konuyu önce bir meslektaşınıza açıklamayı deneyin +- Yalnızca kullanıcının belirli bir konu için gerçekten bilmesi gereken bilgileri sağlayın +- Bilgilerinizin doğru olduğundan emin olun. Her kodu test edin +- Kısa ve öz olun - yazdıklarınızı ikiye bölün. Ve sonra tekrar yapmaktan çekinmeyin +- Kalın yazı tiplerinden aşağıdaki gibi çerçevelere kadar vurgulamayı idareli kullanın `.[note]` +- Kodda [Kodlama Standardını |Coding Standard] Takip Edin -Yazım süreci boyunca bu noktaları aklınızda tutun. Belgeler [Texy! |https://texy.info] dilinde yazılmıştır, bu nedenle [sözdizimini |syntax] öğrenin. Makaleyi yazarken önizleme yapmak için https://editor.nette.org/ adresindeki dokümantasyon editörünü kullanabilirsiniz. +Ayrıca, [sözdizimini |syntax] öğrenin. Yazım sırasında makalenin önizlemesi için önizleme [düzenleyicisini |https://editor.nette.org/] kullanabilirsiniz. -Daha önce listelenen genel yazım kuralları arasında lütfen aşağıdakilere bağlı kalın: -- Kodunuz [Kodlama Standardına |Coding Standard] uygun olmalıdır. -- Değişkenlerin, sınıfların ve yöntemlerin adlarını İngilizce olarak yazın. -- Ad alanlarından yalnızca ilk bahsedildiğinde bahsedilmelidir. -- Kodu kaydırma çubuklarının görüntülenmeyeceği şekilde biçimlendirmeye çalışın. -- Her türlü vurgulayıcıdan uzak durun, kalın yazıdan `.[note]` kutular. -- Belgelerden sadece belgelere veya `www` adresine bakın. +Dil Mutasyonları .[#toc-language-mutations] +------------------------------------------- +İngilizce birincil dildir, bu nedenle değişiklikleriniz İngilizce olmalıdır. Eğer İngilizce sizin için uygun değilse, [DeepL Translator |https://www.deepl.com/translator] 'ı kullanın ve diğerleri metninizi kontrol etsin. -Dokümantasyon Yapısı .[#toc-documentation-structure] ----------------------------------------------------- +Düzenlemenizin onaylanması ve ince ayarlarının yapılmasının ardından diğer dillere çeviri otomatik olarak yapılacaktır. + + +Önemsiz Düzenlemeler .[#toc-trivial-edits] +------------------------------------------ + +Belgelere katkıda bulunmak için [GitHub |https://github.com]'da bir hesabınızın olması gerekir. -Belgelerin tamamı GitHub'da [nette/docs |https://github.com/nette/docs] deposunda barındırılmaktadır. Bu depo, dokümantasyonun sürümüne göre dallara ayrılmıştır, örneğin `doc-3.1` dalı 3.1 sürümünün dokümantasyonunu içerir. Bir de nette.org'un diğer alt alan adlarının içeriğini içeren `nette.org` dalı vardır. +Belgelerde küçük bir değişiklik yapmanın en kolay yolu, her sayfanın sonundaki bağlantıları kullanmaktır: -Her şube daha sonra birkaç klasöre ayrılır: +- *GitHub'da göster* sayfanın GitHub'daki kaynak sürümünü açar. Ardından `E` düğmesine basın ve düzenlemeye başlayabilirsiniz (GitHub'da oturum açmış olmanız gerekir) +- Önizlemeyi aç* son görsel formu hemen görebileceğiniz bir düzenleyici açar -* `cs` ve `en`: her dil sürümü için dokümantasyon dosyaları içerir -* `files`: dokümantasyon sayfalarına gömülebilen resimler + [Önizleme düzenleyicisi |https://editor.nette.org/] değişiklikleri doğrudan GitHub'a kaydetme özelliğine sahip olmadığından, kaynak metni panoya kopyalamanız (*Panoya kopyala düğmesini* kullanarak) ve ardından GitHub'daki düzenleyiciye yapıştırmanız gerekir. +Düzenleme alanının altında gönderim için bir form bulunmaktadır. Burada, düzenlemenizin nedenini kısaca özetlemeyi ve açıklamayı unutmayın. Gönderdikten sonra, daha fazla düzenlenebilen bir çekme isteği (PR) oluşturulur. -Uzantısı olmayan bir dosyanın yolu, belgelerdeki bir sayfanın URL'sine karşılık gelir. Böylece, `en/quickstart/single-post.texy` dosyası `doc.nette.org/en/quickstart/single-post` URL'sine sahip olacaktır. +Daha Büyük Düzenlemeler .[#toc-larger-edits] +-------------------------------------------- -Katkıda Bulunmak .[#toc-contributing] -------------------------------------- +Yalnızca GitHub arayüzüne güvenmek yerine Git sürüm kontrol sistemi ile çalışmanın temellerine aşina olmak daha uygundur. Git'e aşina değilseniz, [git - the simple guide |https://rogerdudler.github.io/git-guide/] 'a başvurabilir ve mevcut birçok [grafik istemciden |https://git-scm.com/downloads/guis] birini kullanmayı düşünebilirsiniz. -Belgelere katkıda bulunmak için [GitHub |https://github.com] 'da bir hesabınızın olması ve Git'in temellerini bilmeniz gerekir. Git'e aşina değilseniz, hızlı kılavuza göz atabilirsiniz: [git - basit kılavuz |https://rogerdudler.github.io/git-guide/] veya birçok grafik araçtan birini kullanın: [GIT - GUI istemcileri |https://git-scm.com/downloads/guis]. +Belgeleri aşağıdaki şekilde düzenleyin: -Doğrudan GitHub arayüzünde basit değişiklikler yapabilirsiniz. Ancak, [nette/docs |https://github.com/nette/docs] deposunun bir çatalını oluşturmak ve bilgisayarınıza klonlamak daha uygundur. Ardından uygun dalda değişiklikler yapın, değişikliği işleyin, GitHub'ınıza itin ve orijinal `nette/docs` deposuna bir çekme isteği gönderin. +1) GitHub'da [nette/docs |https://github.com/nette/docs] deposunun bir [çatalını |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] oluşturun +2) bu depoyu bilgisayarınıza [klonlayın |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] +3) daha sonra, [uygun dalda |#Documentation Structure]değişiklikler yapın +4) [Code-Checker |code-checker:] aracını kullanarak metinde fazladan boşluk olup olmadığını kontrol edin +5) değişiklikleri kaydedin (işleyin) +6) değişikliklerden memnunsanız, bunları GitHub'a çatalınıza gönderin +7) oradan, bir [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) oluşturarak bunları `nette/docs` deposuna gönderin + +Öneriler içeren yorumlar almak yaygındır. Önerilen değişiklikleri takip edin ve bunları dahil edin. Önerilen değişiklikleri yeni taahhütler olarak ekleyin ve GitHub'a yeniden gönderin. Asla sadece mevcut bir talebi değiştirmek için yeni bir talep oluşturmayın. + + +Dokümantasyon Yapısı .[#toc-documentation-structure] +---------------------------------------------------- -Her çekme isteğinden önce, metindeki fazladan boşlukları kontrol etmek için [Code-Checker |code-checker:] 'ı çalıştırmak iyi bir fikirdir. +Belgelerin tamamı GitHub'da [nette/docs |https://github.com/nette/docs] deposunda yer almaktadır. Güncel sürüm master dalında, eski sürümler ise `doc-3.x`, `doc-2.x` gibi dallarda yer almaktadır. -{{priority: -1}} +Her şubenin içeriği, ayrı dokümantasyon alanlarını temsil eden ana klasörlere bölünmüştür. Örneğin, `application/` https://doc.nette.org/en/application 'a, `latte/` https://latte.nette.org 'a, vb. karşılık gelir. Bu klasörlerin her biri dil mutasyonlarını temsil eden alt klasörler (`cs`, `en`, ...) ve isteğe bağlı olarak dokümantasyondaki sayfalara eklenebilecek resimler içeren bir `files` alt klasörü içerir. diff --git a/contributing/uk/documentation.texy b/contributing/uk/documentation.texy index 40763297ff..f70e43a7c8 100644 --- a/contributing/uk/documentation.texy +++ b/contributing/uk/documentation.texy @@ -1,53 +1,69 @@ -Написання документації -********************** +Долучайтеся до створення документації +************************************* .[perex] -Внесок у документацію - це один з багатьох способів допомогти Nette. Це також один з найбільш корисних видів діяльності, оскільки ви допомагаєте іншим зрозуміти фреймворк. +Внесок у документацію є одним з найцінніших видів діяльності, оскільки він допомагає іншим зрозуміти структуру. Як писати? .[#toc-how-to-write] ------------------------------- -Документація в першу чергу призначена для людей, які тільки знайомляться з темою. Тому вона має відповідати кільком важливим пунктам: +Документація в першу чергу призначена для людей, які є новачками в темі. Тому вона повинна відповідати декільком важливим пунктам: -- **При написанні документації починайте з простого і загального, а в кінці переходьте до більш складних тем.**. -- Надавайте лише ту інформацію, яку користувачеві дійсно потрібно знати про тему. -- Переконайтеся, що ваша інформація дійсно правдива. Перш ніж наводити приклад, спочатку протестуйте його. -- Будьте лаконічними - скоротіть те, що ви пишете, наполовину. А потім не соромтеся повторити це ще раз. -- Намагайтеся пояснити питання якомога краще. Наприклад, спробуйте спочатку пояснити тему колезі. +- Починайте з простих і загальних тем. Переходьте до більш складних тем наприкінці +- Намагайтеся пояснювати тему якомога зрозуміліше. Наприклад, спробуйте спочатку пояснити тему колезі +- Надавайте лише ту інформацію, яку користувачеві дійсно потрібно знати з даної теми +- Переконайтеся, що ваша інформація точна. Тестуйте кожен код +- Будьте лаконічними - скорочуйте те, що пишете, вдвічі. А потім не соромтеся робити це ще раз +- Економно використовуйте виділення, від напівжирного шрифту до рамок, таких як `.[note]` +- Дотримуйтесь [стандарту кодування |Coding Standard] в коді -Пам'ятайте про ці моменти протягом усього процесу написання. Документація написана мовою [Texy! |https://texy.info], тому вивчіть її [синтаксис |syntax]. Ви можете скористатися редактором документації на https://editor.nette.org/, щоб переглянути статтю під час її написання. +Також вивчайте [синтаксис |syntax]. Для попереднього перегляду статті під час написання ви можете скористатися [редактором попереднього перегляду |https://editor.nette.org/]. -Серед загальних правил написання документації, перерахованих вище, будь ласка, дотримуйтесь наступних: -- Ваш код повинен відповідати [Стандарту кодування |Coding Standard]. -- Пишіть назви змінних, класів і методів англійською мовою. -- Простори імен мають бути згадані лише при першій згадці. -- Намагайтеся форматувати код так, щоб не відображалися смуги прокрутки. -- Відмовтеся від усіх видів виділень, від напівжирного шрифту до `.[note]` рамок. -- З документації посилайтеся тільки на документацію або `www`. +Мовні мутації .[#toc-language-mutations] +---------------------------------------- +Англійська є основною мовою, тому ваші зміни повинні бути англійською. Якщо англійська не є вашою сильною стороною, скористайтеся [DeepL Перекладачем |https://www.deepl.com/translator], і інші перевірять ваш текст. -Структура документації .[#toc-documentation-structure] ------------------------------------------------------- +Переклад на інші мови буде зроблено автоматично після затвердження та уточнення вашої правки. + + +Тривіальні правки .[#toc-trivial-edits] +--------------------------------------- + +Щоб зробити свій внесок у документацію, вам потрібно мати обліковий запис на [GitHub |https://github.com]. -Повна документація розміщена на GitHub у репозиторії [nette/docs |https://github.com/nette/docs]. Це сховище розділене на гілки відповідно до версії документації, наприклад, гілка `doc-3.1` містить документацію для версії 3.1. А ще є гілка `nette.org`, яка містить вміст інших субдоменів nette.org. +Найпростіший спосіб внести невелику зміну в документацію - скористатися посиланнями в кінці кожної сторінки: -Кожна гілка поділяється на кілька папок: +- *Показати на GitHub* відкриває вихідну версію сторінки на GitHub. Потім просто натисніть кнопку `E` і ви можете почати редагування (ви повинні бути зареєстровані на GitHub) +- *Відкрити попередній перегляд* відкриває редактор, де ви можете одразу побачити остаточний візуальний вигляд -* `cs` і `en`: містять файли документації для кожної мовної версії -* `files`: зображення, які можна вставити на сторінки документації +Оскільки [редактор поперед |https://editor.nette.org/] нього перегляду не має можливості зберігати зміни безпосередньо на GitHub, вам потрібно скопіювати вихідний текст в буфер обміну (за допомогою кнопки *Копіювати в буфер обміну*), а потім вставити його в редактор на GitHub. +Під полем для редагування знаходиться форма для відправки. Тут не забудьте коротко підсумувати і пояснити причину вашого редагування. Після відправки створюється так званий pull request (PR), який можна надалі редагувати. -Шлях до файлу без розширення відповідає URL-адресі сторінки в документації. Таким чином, файл `en/quickstart/single-post.texy` матиме URL `doc.nette.org/en/quickstart/single-post`. +Більші редагування .[#toc-larger-edits] +--------------------------------------- -Дописувач .[#toc-contributing] ------------------------------- +Доцільніше бути знайомим з основами роботи з системою контролю версій Git, ніж покладатися виключно на інтерфейс GitHub. Якщо ви не знайомі з Git'ом, ви можете звернутися до [git - простий |https://rogerdudler.github.io/git-guide/] посібник і розглянути можливість використання одного з багатьох доступних [графічних клієнтів |https://git-scm.com/downloads/guis]. -Щоб зробити внесок у документацію, ви повинні мати обліковий запис на [GitHub |https://github.com] і знати основи Git'у. Якщо ви не знайомі з Git'ом, ви можете ознайомитися з коротким керівництвом: [git - простий |https://rogerdudler.github.io/git-guide/] посібник, або скористатися одним з багатьох графічних інструментів: [GIT - графічні клієнти |https://git-scm.com/downloads/guis]. +Редагуйте документацію наступним чином: -Ви можете вносити прості зміни безпосередньо в інтерфейсі GitHub. Однак, зручніше створити форк репозиторію [nette/docs |https://github.com/nette/docs] і клонувати його на свій комп'ютер. Потім внести зміни у відповідній гілці, зафіксувати зміни, перенести на свій GitHub і відправити pull request до оригінального репозиторію `nette/docs`. +1) на GitHub створіть [форк |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] сховища [nette/docs |https://github.com/nette/docs] +2) [клонуйте |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] цей репозиторій на свій комп'ютер +3) потім внесіть зміни у [відповідній гілці |#Documentation Structure] +4) перевірте наявність зайвих пробілів у тексті за допомогою інструменту [Code-Checker |code-checker:] +5) збережіть (зафіксуйте) зміни +6) якщо зміни вас влаштовують, перенесіть їх на GitHub у свій форк +7) звідти відправте їх до репозиторію `nette/docs`, створивши [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) + +Зазвичай ви отримуєте коментарі з пропозиціями. Відстежуйте запропоновані зміни та вносьте їх. Додайте запропоновані зміни як нові коміти і повторно надішліть їх на GitHub. Ніколи не створюйте новий пул-запит лише для того, щоб змінити існуючий. + + +Структура документації .[#toc-documentation-structure] +------------------------------------------------------ -Перед кожним pull-запитом бажано запускати [Code-Checker |code-checker:], щоб перевірити наявність зайвих пробілів у тексті. +Вся документація знаходиться на GitHub в репозиторії [nette/docs |https://github.com/nette/docs]. Поточна версія знаходиться в головній гілці, тоді як старіші версії знаходяться в гілках `doc-3.x`, `doc-2.x`. -{{priority: -1}} +Вміст кожної гілки розділено на основні папки, що представляють окремі області документації. Наприклад, `application/` відповідає https://doc.nette.org/en/application, `latte/` відповідає https://latte.nette.org і т.д. Кожна з цих папок містить підпапки, що представляють мовні мутації (`cs`, `en`, ...) і додатково підпапку `files` з зображеннями, які можна вставляти на сторінки документації. From 2af8489d334e4e7451f12567bd96fd35fe2f2b8b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 8 Apr 2023 17:50:04 +0200 Subject: [PATCH 06/47] contributing/code: rewritten --- contributing/bg/code.texy | 142 +++++++++++++++++++----------------- contributing/cs/code.texy | 138 ++++++++++++++++++----------------- contributing/de/code.texy | 144 +++++++++++++++++++------------------ contributing/el/code.texy | 144 +++++++++++++++++++------------------ contributing/en/code.texy | 144 +++++++++++++++++++------------------ contributing/es/code.texy | 142 +++++++++++++++++++----------------- contributing/fr/code.texy | 144 +++++++++++++++++++------------------ contributing/hu/code.texy | 144 +++++++++++++++++++------------------ contributing/it/code.texy | 142 +++++++++++++++++++----------------- contributing/pl/code.texy | 142 +++++++++++++++++++----------------- contributing/pt/code.texy | 144 +++++++++++++++++++------------------ contributing/ro/code.texy | 146 ++++++++++++++++++++------------------ contributing/ru/code.texy | 142 +++++++++++++++++++----------------- contributing/sl/code.texy | 144 +++++++++++++++++++------------------ contributing/tr/code.texy | 144 +++++++++++++++++++------------------ contributing/uk/code.texy | 142 +++++++++++++++++++----------------- 16 files changed, 1208 insertions(+), 1080 deletions(-) diff --git a/contributing/bg/code.texy b/contributing/bg/code.texy index 4a25357eec..b4105cf6ce 100644 --- a/contributing/bg/code.texy +++ b/contributing/bg/code.texy @@ -1,110 +1,118 @@ -Предлагане на промяна в кодекса -******************************* +Принос към кода +*************** -Nette Framework използва Git и [GitHub |https://github.com/nette/nette] за поддържане на базата с код. Най-добрият начин да дадете своя принос е да запишете промените си в собственото си разклонение и след това да направите заявка за изтегляне в GitHub. В този документ са обобщени основните стъпки за успешно допринасяне. +.[perex] +Планирате да дадете своя принос към рамката на Nette и трябва да се запознаете с правилата и процедурите? Това ръководство за начинаещи ще ви преведе през стъпките за ефективно допринасяне към кода, работа с хранилища и въвеждане на промени. -Подготовка на средата .[#toc-preparing-environment] -=================================================== +Процедура .[#toc-procedure] +=========================== -Започнете с [разклоняване на |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette в GitHub |https://github.com/nette]. Внимателно [настройте |https://help.github.com/en/github/getting-started-with-github/set-up-git] локалната си среда на Git, конфигурирайте потребителското си име и имейл, тези данни ще идентифицират промените ви в историята на Nette Framework. +За да допринесете към кода, е необходимо да имате акаунт в [GitHub |https://github.com] и да сте запознати с основите на работата със системата за контрол на версиите Git. Ако не сте запознати с Git, можете да разгледате [ръководство git - the simple guide |https://rogerdudler.github.io/git-guide/] и да помислите за използване на някой от многото [графични клиенти |https://git-scm.com/downloads/guis]. -Работа по вашата кръпка .[#toc-working-on-your-patch] -===================================================== +Подготовка на средата и хранилището .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------------ + +1) В GitHub създайте [разклонение на |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [хранилището на пакета |www:packages], който възнамерявате да модифицирате +2) [Клонирайте |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] това хранилище на вашия компютър +3) Инсталирайте зависимостите, включително [Nette Tester |tester:], като използвате командата `composer install` +4) Проверете дали тестовете работят, като стартирате `composer tester` +5) Създайте [нов клон |#New Branch], базиран на последната публикувана версия + + +Внедряване на собствени промени .[#toc-implementing-your-own-changes] +--------------------------------------------------------------------- + +Сега можете да направите собствени промени в кода: + +1) Извършете желаните промени и не забравяйте за тестовете +2) Уверете се, че тестовете се изпълняват успешно, като използвате `composer tester` +3) Проверете дали кодът отговаря на [стандартите за кодиране |#coding standards] +4) Запазете (commit) промените с описание в [този формат |#Commit Description] + +Можете да създадете няколко коммита, по един за всяка логическа стъпка. Всеки commit трябва да е смислен сам по себе си. -Преди да започнете да работите по своята кръпка, създайте нов клон за промените си. -```shell -git checkout -b new_branch_name -``` -Можете да работите върху промяната на кода си. +Изпращане на промени .[#toc-submitting-changes] +----------------------------------------------- -Ако е възможно, направете промените от последната публикувана версия. +След като сте доволни от промените, можете да ги изпратите: +1) Изпратете промените в GitHub към вашето разклонение +2) Оттам ги изпратете в хранилището на Nette, като създадете [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Предоставете [достатъчно информация |#pull request description] в описанието -Тестване на промените .[#toc-testing-your-changes] -================================================== -Трябва да инсталирате Nette Tester. Най-лесният начин е да извикате `composer install` в главното хранилище. Сега трябва да можете да стартирате тестове с `./vendor/bin/tester` в терминала. +Включване на обратна връзка .[#toc-incorporating-feedback] +---------------------------------------------------------- -Някои тестове може да се провалят поради липса на php.ini. Затова трябва да извикате runner-а с параметър -c и да посочите пътя до php.ini, например `./vendor/bin/tester -c ./tests/php.ini`. +Вашите ангажименти вече са видими за другите. Обичайно е да получавате коментари с предложения: -След като успеете да стартирате тестовете, можете да реализирате свои собствени или да промените провалянето, за да съответства на новото поведение. Прочетете повече за тестването с Nette Tester в [страницата с документация |tester:]. +1) Следете предложените промени +2) Включете ги като нови коммити или [ги слейте с предишните |https://help.github.com/en/github/using-git/about-git-rebase] +3) Изпратете отново коммитите в GitHub и те автоматично ще се появят в заявката за изтегляне + +Никога не създавайте нова заявка за изтегляне, за да промените съществуваща. + + +Документация .[#toc-documentation] +---------------------------------- + +Ако сте променили функционалност или сте добавили нова, не забравяйте да [я добавите и в документацията |documentation]. + + +Нов клон .[#toc-new-branch] +=========================== + +Ако е възможно, направете промени спрямо последната публикувана версия, т.е. последния таг в клона. За тага v3.2.1 създайте клон, като използвате тази команда: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Стандарти за кодиране .[#toc-coding-standards] ============================================== -Вашият код трябва да следва [стандартите за кодиране, |coding standard] използвани в Nette Framework. Това е лесно, тъй като има автоматична програма за проверка и поправка. Той може да бъде инсталиран чрез Composer в избрана от вас глобална директория: +Вашият код трябва да отговаря на [стандартите за кодиране, |coding standard] използвани в Nette Framework. Налице е автоматичен инструмент за проверка и поправка на кода. Можете да го инсталирате **глобално** чрез Composer в папка по ваш избор: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Сега трябва да можете да стартирате инструмента в терминала. Например, тази команда проверява и поправя кода в папките `src` и `tests` в текущата директория: +Сега трябва да можете да стартирате инструмента в терминала. Първата команда проверява, а втората поправя кода в папките `src` и `tests` в текущата директория: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Предаване на промените .[#toc-committing-the-changes] -===================================================== - -След като сте променили кода, трябва да предадете промените. Създайте повече предавания, по едно за всяка логическа стъпка. Всеки commit трябва да може да се използва в този си вид - без други commit-ове. Така че съответните тестове също трябва да бъдат включени в същия commit. +Ангажимент Описание .[#toc-commit-description] +============================================== -Моля, проверете два пъти дали кодът ви отговаря на правилата: -- Кодът не генерира грешки -- Кодът ви не нарушава никакви тестове. -- Промяната в кода ви е тествана. -- Не извършвате безполезни промени в бялото пространство. +В Nette темите на ангажиментите имат следния формат: `Presenter: fixed AJAX detection [Closes #69]` -Съобщението за предаване трябва да следва формата `Latte: fixed multi template rendering [Closes # 69]` т.е: - област, последвана от двоеточие -- целта на предаването в миналото, ако е възможно, започнете с "добавено.", "поправено.", "префактурирано.", променено, премахнато -- евентуална връзка към тракер на проблеми -- ако предаването отменя обратната съвместимост, добавете "BC break". -- може да има един свободен ред след темата и по-подробно описание, включително връзки към форума. - +- цел на ангажимента в минало време; ако е възможно, започвайте с думи като: added, fixed, refactored, changed, removed +- ако ангажиментът нарушава обратната съвместимост, добавете "BC break". +- всякаква връзка с органа за проследяване на проблеми, като например `(#123)` или `[Closes #69]` +- след темата може да има един празен ред, последван от по-подробно описание, включително например връзки към форума -Изтегляне на искания за допълнения .[#toc-pull-requesting-the-commits] -====================================================================== -Ако сте доволни от промените в кода си и извършените от вас промени, трябва да изпратите промените в GitHub. +Описание на заявката за изтегляне .[#toc-pull-request-description] +================================================================== -```shell -git push origin new_branch_name -``` +Когато създавате заявка за изтегляне, интерфейсът на GitHub ще ви позволи да въведете заглавие и описание. Посочете кратко заглавие, а в описанието включете възможно най-много информация за причините за вашата промяна. -Промените са публични, но трябва да предложите промените си за интегриране в главния клон на Nette. За целта направете [заявка за изтегляне |https://help.github.com/articles/creating-a-pull-request]. -Всяка заявка за изтегляне има заглавие и описание. Моля, посочете някакво описателно заглавие. То често е подобно на името на клона, например "Защита на сигналите срещу CSRF атака". +Също така посочете в заглавието дали става въпрос за нова функция или за поправка на грешка и дали тя може да предизвика проблеми с обратната съвместимост (BC break). Ако има свързан проблем, поставете връзка към него, така че той да бъде затворен при одобряване на заявката за промяна. -Описанието на заявката за изтегляне трябва да съдържа някаква по-конкретна информация за промените в кода ви: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Моля, променете информационната таблица, за да съответства на вашата заявка за изтегляне. Коментари към всеки елемент от списъка: -- Пише дали заявката за изтегляне добавя **функция** или е **поправка на грешка**. -- Позовава се на евентуално **свързан проблем**, който ще бъде затворен след обединяването на заявката за изтегляне. -- Казва дали заявката за изтегляне се нуждае от **промени в документацията**, ако да, предоставя препратки към съответните заявки за изтегляне. Не е необходимо да предоставяте промените в документацията веднага, обаче заявката за изтегляне няма да бъде обединена, ако са необходими промени в документацията. Промяната в документацията трябва да бъде подготвена за документация на английски език, други езикови мутации не са задължителни. -- Казва, ако заявката за изтегляне създава **прекъсване на БК**. Моля, считайте всичко, което променя публичния интерфейс, за прекъсване на БК. - -Окончателната таблица може да изглежда така: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Преработване на промените .[#toc-reworking-your-changes] -======================================================== -Много често се случва да получавате коментари към промените в кода си. Моля, опитайте се да следвате предложените промени и преработете своите ангажименти, за да го направите. Можете да ангажирате предложените промени като нови ангажименти и след това да ги смачкате към предишните. Вижте главата [Интерактивно преизчисляване в |https://help.github.com/en/github/using-git/about-git-rebase] GitHub. След като ребазирате промените си, форсирайте промените си към отдалеченото си разклонение, като всичко ще се разпространи автоматично към заявката за изтегляне. {{priority: -1}} diff --git a/contributing/cs/code.texy b/contributing/cs/code.texy index 4730f3179d..67d5b97ead 100644 --- a/contributing/cs/code.texy +++ b/contributing/cs/code.texy @@ -1,110 +1,118 @@ -Navrhování změn v kódu -********************** +Jak přispět do kódu +******************* -Nette Framework používá Git a [GitHub |https://github.com/nette/nette] jako úložiště pro kód. Nejlepší způsob, jak přispět, je provést změny ve vaší větvi a poté poslat pull request (PR) na GitHub. Tento dokument shrnuje hlavní kroky jak na to. +.[perex] +Chystáte se přispět do Nette Frameworku a potřebujete se zorientovat v pravidlech a postupech? Tento průvodce pro začátečníky vám krok za krokem ukáže, jak efektivně přispívat do kódu, pracovat s repozitáři a implementovat změny. -Příprava prostředí -================== +Postup +====== -Nejprve si [forkněte |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette z GitHubu |https://github.com/nette]. Pečlivě [nastavte |https://help.github.com/en/github/getting-started-with-github/set-up-git] prostředí Gitu, nakonfigurujte své uživatelské jméno a e-mail, tyto údaje vás budou identifikovat v historii změn kódu. +Pro přispívání do kódu je nezbytné mít účet na [GitHub|https://github.com] a být obeznámen se základy práce s verzovacím systémem Git. Pokud neovládáte práci s Gitem, můžete se podívat na průvodce [git - the simple guide |https://rogerdudler.github.io/git-guide/] a případně využít některého z mnoha [grafických klientů |https://git-scm.com/downloads/guis]. -Editace kódu -============ +Příprava prostředí a repozitáře +------------------------------- -Než začnete programovat, vytvořte si novou větev. -```shell -git checkout -b new_branch_name -``` +1) na GitHubu si vytvořte [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] repositáře [balíčku |www:packages], který se chystáte upravit +2) tento repositář [naklonujete |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] na svůj počítač +3) nainstalujte závislosti, včetně [Nette Testeru |tester:], pomocí příkazu `composer install` +4) zkontrolujte, že testy fungují, spuštěním `composer tester` +5) vytvořte si [novou větev |#Nová větev] založenou na poslední vydané verzi + + +Implementace vlastních změn +--------------------------- + +Nyní můžete provést své vlastní úpravy kódu: + +1) naprogramujte požadované změny a nezapomeňte na testy +2) ujistěte se, že testy proběhnou úspěšně, pomocí `composer tester` +3) zkontrolujte, zda kód splňuje [kódovací standard|#Coding Standards] +4) změny uložte (commitněte) s popisem v [tomto formátu|#Popis komitu] + +Můžete vytvořit několik commitů, jeden pro každý logický krok. Každý commit by měl být smysluplný samostatně. + + +Odeslání změn +------------- + +Jakmile budete se změnami spokojeni, můžete je odeslat: + +1) odešlete (pushněte) změny na GitHub do vašeho forku +2) odtud je odešlete do Nette repositáře vytvořením [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) uveďte v popisu [dostatek informací|#popis pull requestu] + + +Zapracování připomínek +---------------------- + +Vaše commity nyní uvidí i ostatní. Je běžné, že dostanete komentáře s připomínkami: -A můžete začít upravovat kód. +1) sledujte navrhované úpravy +2) zapracujte je jako nové commity nebo je [slučte s předchozími |https://help.github.com/en/github/using-git/about-git-rebase] +3) znovu odešlete commity na GitHub a automaticky se objeví v pull requestu -Pokud je to možné, dělejte změny oproti poslední vydané verzi. +Nikdy nevytvářejte nový pull request kvůli úpravě stávajícího. -Testování změn -============== +Dokumentace +----------- -Nainstalujte si Nette Tester. Nejjednodušší je zavolat `composer install` v kořenovém adresáři repositáře. Nyní byste měli být schopni spustit testy v terminálu pomocí `./vendor/bin/tester`. +Pokud jste změnili funkčnost nebo přidali novou, nezapomeňte ji také [přidat do dokumentace |documentation]. -Některé testy mohou selhat kvůli chybějícímu php.ini. Proto byste měli volat Tester s parametrem -c a zadat cestu k php.ini, například `./vendor/bin/tester -c ./tests/php.ini`. -Poté, co funguje spouštění testů, můžete implementovat vlastní změny v kódu. Další informace o testování pomocí nástroje Nette Tester najdete v [jeho dokumentaci |tester:]. +Nová větev +========== + +Pokud je to možné, provádějte změny vůči poslední vydané verzi, tj. poslednímu tagu v dané větvi. Pro tag `v3.2.1` vytvoříte větev tímto příkazem: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Coding Standards ================ -Váš kód musí splňovat [coding standard] používaný v Nette Framework. Je to snadné, protože existuje automatický nástroj pro kontrolu a opravu kódu. Lze jej nainstalovat přes Composer do vámi zvolené globální složky: +Váš kód musí splňovat [coding standard] používaný v Nette Framework. Pro kontrolu a opravu kódu je k dispozici automatický nástroj. Lze jej nainstalovat přes Composer **globálně** do vámi zvolené složky: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Nyní byste měli mít možnost spustit nástroj v terminálu. Tímto příkazem například zkontrolujete a opravíte kód ve složkách `src` a `tests` v aktuálním adresáři: +Nyní byste měli mít možnost spustit nástroj v terminálu. Prvním příkazem zkontrolujete a druhým i opravíte kód ve složkách `src` a `tests` v aktuálním adresáři: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Komitování změn -=============== - -Po úpravě kódu jej tzv. komitujete. Komitů klidně vytvořte několik, pro každý logický krok jeden. Každý commit by měl být smysluplný sám o sobě. Měl by zahrnovat i testy. +Popis komitu +============ -Zkontrolujte prosím, zda váš kód odpovídá pravidlům: -- Kód nevytváří žádné chyby -- Neporušuje žádné testy. -- Změny v kódu jsou testované. -- Neděláte zbytečné změny v bílém prostoru. +V Nette mají předměty komitů formát: `Presenter: fixed AJAX detection [Closes #69]` -Popis komitu by měl odpovídat formátu `Latte: fixed multi-template rendering [Closes #69]`, tj. - oblast následovaná dvojtečkou - účel commitu v minulém čase, je-li to možné, začněte slovem: "added .(přidaná nová vlastnost)", "fixed .(oprava)", "refactored .(změna v kódu beze změny chování)", changed, removed -- případná vazba na issue tracker - pokud commit přeruší zpětnou kompatibilitu, doplňte "BC break" -- za subjektem může být jeden volný řádek a podrobnější popis včetně odkazů na fórum. +- případná vazba na issue tracker jako `(#123)` nebo `[Closes #69]` +- za subjektem může následovat jeden volný řádek a poté podrobnější popis včetně třeba odkazů na fórum -Posílání Pull requestu -====================== +Popis pull requestu +=================== -Pokud jste se změnami spokojeni, odešlete je na GitHub. +Při vytváření pull requestu vám rozhraní GitHubu umožní zadat název a popis. Uveďte výstižný název a v popisu poskytněte co nejvíce informací o důvodech pro vaši změnu. -```shell -git push origin new_branch_name -``` +Zobrazí se také záhlaví, kde specifikujte, zda se jedná o novou funkci nebo opravu chyby a zda může dojít k narušení zpětné kompatibility (BC break). Pokud je k dispozici související problém (issue), odkazujte na něj, aby byl uzavřen po schválení pull requestu. -Kód už je veřejně dostupný, ale musíte je odeslat do hlavní větve (masteru) v repozitáři Nette. Udělejte tzv. [pull request |https://help.github.com/articles/creating-a-pull-request]. -Každá žádost má název a popis. Uveďte prosím výstižný název. Například "Zabezpečení signálů proti útoku CSRF". - -Popis pull requestu by měl obsahovat další specifické informace: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Upravte prosím tabulku informací tak, aby odpovídala vašemu pull requestu. -- uveďte, jestli jde o novou funkci, nebo o bugfix -- odkažte na případnou **související issue**, která bude uzavřena po schválení pull requestu. -- uveďte, zda požadavek potřebuje **dokumentační změny**, pokud ano, uveďte odkazy na příslušné issue. Změnu dokumentace nemusíte dělat okamžitě, ale pull request nebude přijat, pokud je zapotřebí změnit dokumentaci. Změna dokumentace musí být připravena pro anglickou dokumentaci, jiné jazykové mutace jsou nepovinné. -- uveďte, zda změna v kódu způsobí **BC break**. Vezměte prosím v úvahu všechno, co jej může způsobit. - -Konečná tabulka by mohla vypadat takto: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Přepracování změn -================= -Je běžné, že budete dostávat komentáře s připomínkami. Sledujte navrhované změny a zapracujte je. Navrhované změny můžete přidávat jako nové commity a sloučit je s předchozími. Viz kapitola [Interactive rebase |https://help.github.com/en/github/using-git/about-git-rebase] na stránce GitHubu. Poté opět odešlete commity na GitHub a vše se automaticky objeví v pull requestu. {{priority: -1}} diff --git a/contributing/de/code.texy b/contributing/de/code.texy index a9b16442a7..820a1d7d88 100644 --- a/contributing/de/code.texy +++ b/contributing/de/code.texy @@ -1,110 +1,118 @@ -Vorschlag für eine Änderung des Kodex -************************************* +Zum Code beitragen +****************** -Nette Framework verwendet Git und [GitHub |https://github.com/nette/nette] für die Pflege der Codebasis. Der beste Weg, einen Beitrag zu leisten, ist, Ihre Änderungen in Ihren eigenen Fork zu übertragen und dann einen Pull-Request auf GitHub zu stellen. Dieses Dokument fasst die wichtigsten Schritte für einen erfolgreichen Beitrag zusammen. +.[perex] +Planen Sie, zum Nette Framework beizutragen, und müssen Sie sich mit den Regeln und Verfahren vertraut machen? Dieser Leitfaden für Anfänger führt Sie durch die Schritte, um effektiv zum Code beizutragen, mit Repositories zu arbeiten und Änderungen zu implementieren. -Vorbereiten der Umgebung .[#toc-preparing-environment] -====================================================== +Verfahren .[#toc-procedure] +=========================== -Beginnen Sie mit dem [Forking |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] von [Nette auf GitHub |https://github.com/nette]. Richten Sie Ihre lokale Git-Umgebung sorgfältig [ein |https://help.github.com/en/github/getting-started-with-github/set-up-git], konfigurieren Sie Ihren Benutzernamen und Ihre E-Mail-Adresse, da diese Anmeldedaten Ihre Änderungen in der Nette-Framework-Historie identifizieren. +Um zum Code beizutragen, ist es wichtig, ein Konto auf [GitHub |https://github.com] zu haben und mit den Grundlagen der Arbeit mit dem Versionskontrollsystem Git vertraut zu sein. Wenn Sie mit Git nicht vertraut sind, können Sie sich den [Leitfaden "Git - die einfache Anleitung |https://rogerdudler.github.io/git-guide/] " ansehen und die Verwendung eines der vielen [grafischen Clients |https://git-scm.com/downloads/guis] in Betracht ziehen. -Arbeiten Sie an Ihrem Patch .[#toc-working-on-your-patch] -========================================================= +Vorbereiten der Umgebung und des Repositorys .[#toc-preparing-the-environment-and-repository] +--------------------------------------------------------------------------------------------- -Bevor Sie mit der Arbeit an Ihrem Patch beginnen, erstellen Sie einen neuen Zweig für Ihre Änderungen. -```shell -git checkout -b new_branch_name -``` +1) Erstellen Sie auf GitHub einen [Fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] des [Paket-Repositorys |www:packages], das Sie ändern möchten. +2) [Klonen |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] Sie dieses Repository auf Ihren Computer +3) Installieren Sie die Abhängigkeiten, einschließlich [Nette Tester |tester:], mit dem Befehl `composer install` +4) Überprüfen Sie, ob die Tests funktionieren, indem Sie den Befehl `composer tester` +5) Erstellen Sie eine [neue Verzweigung |#New Branch] auf der Grundlage der letzten veröffentlichten Version + + +Eigene Änderungen implementieren .[#toc-implementing-your-own-changes] +---------------------------------------------------------------------- + +Jetzt können Sie Ihre eigenen Code-Anpassungen vornehmen: + +1) Implementieren Sie die gewünschten Änderungen und vergessen Sie dabei nicht die Tests +2) Stellen Sie sicher, dass die Tests erfolgreich laufen. `composer tester` +3) Prüfen Sie, ob der Code den [Kodierungsstandards |#coding standards]entspricht +4) Speichern (Commit) Sie die Änderungen mit einer Beschreibung in [diesem Format |#Commit Description] + +Sie können mehrere Übertragungen erstellen, eine für jeden logischen Schritt. Jeder Commit sollte für sich genommen sinnvoll sein. + + +Einreichen von Änderungen .[#toc-submitting-changes] +---------------------------------------------------- + +Wenn Sie mit den Änderungen zufrieden sind, können Sie sie übermitteln: + +1) Verschieben Sie die Änderungen auf GitHub in Ihren Fork +2) Übermitteln Sie sie von dort aus an das Nette-Repository, indem Sie einen [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) erstellen +3) Geben Sie [ausreichende Informationen |#pull request description] in der Beschreibung an + + +Feedback einbeziehen .[#toc-incorporating-feedback] +--------------------------------------------------- + +Ihre Übertragungen sind nun für andere sichtbar. Es ist üblich, dass Sie Kommentare mit Vorschlägen erhalten: -Sie können an Ihrer Codeänderung arbeiten. +1) Behalten Sie die vorgeschlagenen Änderungen im Auge +2) Fügen Sie sie als neue Commits ein oder [fügen Sie sie mit früheren zusammen |https://help.github.com/en/github/using-git/about-git-rebase] +3) Senden Sie die Commits erneut an GitHub, und sie erscheinen automatisch in der Pull-Anfrage -Wenn möglich, nehmen Sie Änderungen an der zuletzt veröffentlichten Version vor. +Erstellen Sie niemals einen neuen Pull Request, um einen bestehenden zu ändern. -Testen Ihrer Änderungen .[#toc-testing-your-changes] -==================================================== +Dokumentation .[#toc-documentation] +----------------------------------- -Sie müssen Nette Tester installieren. Der einfachste Weg ist der Aufruf von `composer install` im Repository root. Nun sollten Sie in der Lage sein, Tests mit `./vendor/bin/tester` im Terminal auszuführen. +Wenn Sie eine Funktion geändert oder eine neue hinzugefügt haben, vergessen Sie nicht, [diese auch in die Dokumentation aufzunehmen |documentation]. -Einige Tests können aufgrund einer fehlenden php.ini fehlschlagen. Daher sollten Sie den Runner mit dem Parameter -c aufrufen und den Pfad zur php.ini angeben, zum Beispiel `./vendor/bin/tester -c ./tests/php.ini`. -Nachdem Sie die Tests ausführen können, können Sie Ihre eigenen Tests implementieren oder die fehlgeschlagenen Tests an das neue Verhalten anpassen. Lesen Sie mehr über das Testen mit Nette Tester auf der [Dokumentationsseite |tester:]. +Neuer Zweig .[#toc-new-branch] +============================== + +Wenn möglich, nehmen Sie Änderungen an der letzten veröffentlichten Version vor, d.h. am letzten Tag im Zweig. Für das Tag v3.2.1 erstellen Sie einen Zweig mit diesem Befehl: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Kodierungsstandards .[#toc-coding-standards] ============================================ -Ihr Code muss den im Nette Framework verwendeten [Kodierungsstandards |coding standard] entsprechen. Das ist einfach, denn es gibt einen automatischen Checker und Fixer. Er kann über Composer in das von Ihnen gewählte globale Verzeichnis installiert werden: +Ihr Code muss den im Nette Framework verwendeten [Kodierungsstandards |coding standard] entsprechen. Es gibt ein automatisches Tool zur Überprüfung und Korrektur des Codes. Sie können es **global** über Composer in einen Ordner Ihrer Wahl installieren: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Nun sollten Sie in der Lage sein, das Tool im Terminal auszuführen. Dieser Befehl prüft und korrigiert zum Beispiel den Code in den Ordnern `src` und `tests` im aktuellen Verzeichnis: +Nun sollten Sie in der Lage sein, das Tool im Terminal auszuführen. Der erste Befehl überprüft und der zweite korrigiert den Code in den Ordnern `src` und `tests` im aktuellen Verzeichnis: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Übertragen der Änderungen .[#toc-committing-the-changes] -======================================================== +Commit Beschreibung .[#toc-commit-description] +============================================== -Nachdem Sie den Code geändert haben, müssen Sie Ihre Änderungen festschreiben. Erstellen Sie mehrere Commits, einen für jeden logischen Schritt. Jeder Commit sollte so wie er ist verwendbar sein - ohne andere Commits. Daher sollten auch die entsprechenden Tests im selben Commit enthalten sein. +In Nette haben Commit-Themen das folgende Format: `Presenter: fixed AJAX detection [Closes #69]` -Bitte überprüfen Sie, ob Ihr Code den Regeln entspricht: -- Der Code erzeugt keine Fehler -- Ihr Code bricht keine Tests. -- Ihre Codeänderung ist getestet. -- Sie nehmen keine unnötigen Änderungen am Leerraum vor. +- Bereich, gefolgt von einem Doppelpunkt +- Zweck des Commits in der Vergangenheitsform; wenn möglich, beginnen Sie mit Worten wie: added, fixed, refactored, changed, removed +- wenn der Commit die Abwärtskompatibilität bricht, fügen Sie "BC break" hinzu +- jede Verbindung zum Issue Tracker, wie `(#123)` oder `[Closes #69]` +- nach dem Betreff kann eine Leerzeile folgen, gefolgt von einer detaillierteren Beschreibung, z.B. mit Links zum Forum -Die Commit-Nachricht sollte das folgende Format haben `Latte: fixed multi template rendering [Closes # 69]` d. h: -- ein Bereich gefolgt von einem Doppelpunkt -- der Zweck des Commits in der Vergangenheit, wenn möglich, beginnen Sie mit "hinzugefügt.", "behoben.", "überarbeitet.", geändert, entfernt -- eventueller Link zum Issue Tracker -- wenn der Commit die Abwärtskompatibilität aufhebt, füge "BC break" hinzu -- eine freie Zeile nach dem Betreff und eine ausführlichere Beschreibung mit Links zum Forum. +Pull Request Beschreibung .[#toc-pull-request-description] +========================================================== -Pull-Request für die Commits .[#toc-pull-requesting-the-commits] -================================================================ - -Wenn Sie mit Ihren Codeänderungen und Commits zufrieden sind, müssen Sie Ihre Commits auf GitHub veröffentlichen. - -```shell -git push origin new_branch_name -``` +Wenn Sie eine Pull-Anfrage erstellen, können Sie über die GitHub-Schnittstelle einen Titel und eine Beschreibung eingeben. Geben Sie einen prägnanten Titel an und fügen Sie in der Beschreibung so viele Informationen wie möglich über die Gründe für Ihre Änderung ein. -Die Änderungen sind öffentlich zugänglich, aber Sie müssen Ihre Änderungen zur Integration in den Master-Zweig von Nette vorschlagen. Dazu müssen Sie [einen Pull-Request erstellen |https://help.github.com/articles/creating-a-pull-request]. -Jeder Pull Request hat einen Titel und eine Beschreibung. Bitte geben Sie einen beschreibenden Titel an. Er ist oft ähnlich wie der Name des Zweiges, zum Beispiel "Absicherung von Signalen gegen CSRF-Angriffe". +Geben Sie außerdem in der Kopfzeile an, ob es sich um eine neue Funktion oder eine Fehlerbehebung handelt und ob sie zu Problemen mit der Abwärtskompatibilität führen kann (BC-Break). Falls es ein verwandtes Problem gibt, verlinken Sie es, damit es bei Genehmigung des Pull Requests geschlossen wird. -Die Beschreibung der Pull-Anfrage sollte einige spezifischere Informationen über Ihre Codeänderungen enthalten: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Bitte ändern Sie die Informationstabelle, damit sie zu Ihrem Pull Request passt. Kommentare zu jedem Listenpunkt: -- Gibt an, ob der Pull Request ein **Feature** hinzufügt oder ein **Bugfix** ist. -- Verweist auf einen **verwandten Fehler**, der nach dem Zusammenführen des Pull Requests geschlossen werden wird. -- Sagt, ob der Pull Request **Änderungen an der Dokumentation** benötigt, wenn ja, gib Referenzen zu den entsprechenden Pull Requests an. Sie müssen die Dokumentationsänderung nicht sofort zur Verfügung stellen, jedoch wird der Pull Request nicht zusammengeführt, wenn die Dokumentationsänderung benötigt wird. Die Dokumentationsänderung muss für die englische Dokumentation vorbereitet werden, andere Sprachmutationen sind optional. -- Sagt, wenn der Pull Request **einen BC-Break** erzeugt. Bitte betrachten Sie alles, was die öffentliche Schnittstelle ändert, als einen BC-Break. - -Die endgültige Tabelle könnte wie folgt aussehen: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Überarbeitung Ihrer Änderungen .[#toc-reworking-your-changes] -============================================================= -Es ist durchaus üblich, dass Sie Kommentare zu Ihren Codeänderungen erhalten. Bitte versuchen Sie, die vorgeschlagenen Änderungen zu befolgen und Ihre Commits entsprechend zu überarbeiten. Sie können vorgeschlagene Änderungen als neue Commits committen und sie dann mit den vorherigen Commits zusammenführen. Siehe Kapitel [Interaktives Rebase |https://help.github.com/en/github/using-git/about-git-rebase] auf GitHub. Nachdem Sie Ihre Änderungen rebasiert haben, pushen Sie Ihre Änderungen auf Ihren entfernten Fork, alles wird automatisch auf den Pull Request übertragen. {{priority: -1}} diff --git a/contributing/el/code.texy b/contributing/el/code.texy index e59fb50bb4..782bb06fea 100644 --- a/contributing/el/code.texy +++ b/contributing/el/code.texy @@ -1,110 +1,118 @@ -Πρόταση αλλαγής του κώδικα -************************** +Συμβολή στον κώδικα +******************* -Το Nette Framework χρησιμοποιεί το Git και το [GitHub |https://github.com/nette/nette] για τη συντήρηση της βάσης κώδικα. Ο καλύτερος τρόπος για να συνεισφέρετε είναι να δεσμεύσετε τις αλλαγές σας στο δικό σας fork και στη συνέχεια να κάνετε ένα pull request στο GitHub. Αυτό το έγγραφο συνοψίζει τα κύρια βήματα για την επιτυχή συνεισφορά. +.[perex] +Σχεδιάζετε να συνεισφέρετε στο Nette Framework και πρέπει να εξοικειωθείτε με τους κανόνες και τις διαδικασίες; Αυτός ο οδηγός για αρχάριους θα σας καθοδηγήσει στα βήματα για να συνεισφέρετε αποτελεσματικά στον κώδικα, να εργαστείτε με αποθετήρια και να εφαρμόσετε αλλαγές. -Προετοιμασία περιβάλλοντος .[#toc-preparing-environment] -======================================================== +Διαδικασία .[#toc-procedure] +============================ -Ξεκινήστε με το [forking |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [του Nette στο GitHub |https://github.com/nette]. [Ρυθμίστε |https://help.github.com/en/github/getting-started-with-github/set-up-git] προσεκτικά το τοπικό σας περιβάλλον Git, ρυθμίστε το όνομα χρήστη και το email σας, αυτά τα διαπιστευτήρια θα αναγνωρίσουν τις αλλαγές σας στο ιστορικό του Nette Framework. +Για να συνεισφέρετε στον κώδικα, είναι απαραίτητο να έχετε έναν λογαριασμό στο [GitHub |https://github.com] και να είστε εξοικειωμένοι με τα βασικά στοιχεία της εργασίας με το σύστημα ελέγχου εκδόσεων Git. Αν δεν είστε εξοικειωμένοι με το Git, μπορείτε να διαβάσετε το [git - the simple |https://rogerdudler.github.io/git-guide/] guide και να εξετάσετε το ενδεχόμενο να χρησιμοποιήσετε έναν από τους πολλούς [γραφικούς πελάτες |https://git-scm.com/downloads/guis]. -Επεξεργασία του Patch σας .[#toc-working-on-your-patch] -======================================================= +Προετοιμασία του περιβάλλοντος και του αποθετηρίου .[#toc-preparing-the-environment-and-repository] +--------------------------------------------------------------------------------------------------- + +1) Στο GitHub, δημιουργήστε μια [διακλάδωση |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] του [αποθετηρίου πακέτων |www:packages] που σκοπεύετε να τροποποιήσετε +2) [Κλωνοποιήστε |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] αυτό το αποθετήριο στον υπολογιστή σας +3) Εγκαταστήστε τις εξαρτήσεις, συμπεριλαμβανομένου του [Nette Tester |tester:], χρησιμοποιώντας την εντολή `composer install` +4) Επαληθεύστε ότι οι δοκιμές λειτουργούν εκτελώντας την εντολή `composer tester` +5) Δημιουργήστε έναν [νέο κλάδο |#New Branch] με βάση την τελευταία έκδοση που κυκλοφόρησε + + +Εφαρμογή των δικών σας αλλαγών .[#toc-implementing-your-own-changes] +-------------------------------------------------------------------- + +Τώρα μπορείτε να κάνετε τις δικές σας προσαρμογές στον κώδικα: + +1) Εφαρμόστε τις επιθυμητές αλλαγές και μην ξεχνάτε τις δοκιμές +2) Βεβαιωθείτε ότι οι δοκιμές εκτελούνται με επιτυχία χρησιμοποιώντας `composer tester` +3) Ελέγξτε αν ο κώδικας πληροί τα [πρότυπα κωδικοποίησης |#coding standards] +4) Αποθηκεύστε (commit) τις αλλαγές με μια περιγραφή [αυτής της μορφής |#Commit Description] + +Μπορείτε να δημιουργήσετε πολλαπλές δεσμεύσεις, μία για κάθε λογικό βήμα. Κάθε commit θα πρέπει να έχει νόημα από μόνο του. -Πριν ξεκινήσετε να εργάζεστε πάνω στο patch σας, δημιουργήστε ένα νέο κλάδο για τις αλλαγές σας. -```shell -git checkout -b new_branch_name -``` -Μπορείτε να εργαστείτε πάνω στην αλλαγή του κώδικά σας. +Υποβολή αλλαγών .[#toc-submitting-changes] +------------------------------------------ -Αν είναι δυνατόν, κάντε αλλαγές από την τελευταία έκδοση που κυκλοφόρησε. +Αφού είστε ικανοποιημένοι με τις αλλαγές, μπορείτε να τις υποβάλετε: +1) Σπρώξτε τις αλλαγές στο GitHub στο fork σας +2) Από εκεί, υποβάλετε τις στο αποθετήριο Nette δημιουργώντας ένα [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Παρέχετε [επαρκείς πληροφορίες |#pull request description] στην περιγραφή -Δοκιμή των αλλαγών σας .[#toc-testing-your-changes] -=================================================== -Πρέπει να εγκαταστήσετε το Nette Tester. Ο ευκολότερος τρόπος είναι να καλέσετε το `composer install` στη ρίζα του αποθετηρίου. Τώρα θα πρέπει να είστε σε θέση να εκτελέσετε δοκιμές με το `./vendor/bin/tester` στο τερματικό. +Ενσωμάτωση ανατροφοδότησης .[#toc-incorporating-feedback] +--------------------------------------------------------- -Ορισμένες δοκιμές μπορεί να αποτύχουν λόγω έλλειψης του php.ini. Επομένως, θα πρέπει να καλέσετε το runner με την παράμετρο -c και να καθορίσετε τη διαδρομή προς το php.ini, για παράδειγμα `./vendor/bin/tester -c ./tests/php.ini`. +Οι δεσμεύσεις σας είναι τώρα ορατές στους άλλους. Είναι σύνηθες να λαμβάνετε σχόλια με προτάσεις: -Αφού είστε σε θέση να εκτελέσετε τις δοκιμές, μπορείτε να υλοποιήσετε τις δικές σας ή να αλλάξετε την αποτυχία ώστε να ταιριάζει με τη νέα συμπεριφορά. Διαβάστε περισσότερα για τις δοκιμές με το Nette Tester στη [σελίδα τεκμηρίωσης |tester:]. +1) Να παρακολουθείτε τις προτεινόμενες αλλαγές +2) Ενσωματώστε τις ως νέες commits ή [συγχωνεύστε τις με προηγούμενες |https://help.github.com/en/github/using-git/about-git-rebase] +3) Υποβάλετε εκ νέου τα commits στο GitHub, και θα εμφανιστούν αυτόματα στο pull request + +Ποτέ μην δημιουργείτε νέο pull request για να τροποποιήσετε ένα υπάρχον. + + +Τεκμηρίωση .[#toc-documentation] +-------------------------------- + +Εάν έχετε αλλάξει τη λειτουργικότητα ή έχετε προσθέσει μια νέα, μην ξεχάσετε να [την προσθέσετε και στην τεκμηρίωση |documentation]. + + +Νέος κλάδος .[#toc-new-branch] +============================== + +Αν είναι δυνατόν, κάντε αλλαγές σε σχέση με την τελευταία έκδοση που κυκλοφόρησε, δηλαδή την τελευταία ετικέτα του κλάδου. Για την ετικέτα v3.2.1, δημιουργήστε έναν κλάδο χρησιμοποιώντας αυτή την εντολή: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Πρότυπα κωδικοποίησης .[#toc-coding-standards] ============================================== -Ο κώδικάς σας πρέπει να ακολουθεί τα [πρότυπα κωδικοποίησης |coding standard] που χρησιμοποιούνται στο Nette Framework. Είναι εύκολο επειδή υπάρχει αυτοματοποιημένος ελεγκτής & διορθωτής. Μπορεί να εγκατασταθεί μέσω του Composer στον παγκόσμιο κατάλογο που έχετε επιλέξει: +Ο κώδικάς σας πρέπει να πληροί τα [πρότυπα κωδικοποίησης |coding standard] που χρησιμοποιούνται στο Nette Framework. Υπάρχει διαθέσιμο ένα αυτόματο εργαλείο για τον έλεγχο και τη διόρθωση του κώδικα. Μπορείτε να το εγκαταστήσετε **σφαιρικά** μέσω του Composer σε έναν φάκελο της επιλογής σας: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Τώρα θα πρέπει να είστε σε θέση να εκτελέσετε το εργαλείο στο τερματικό. Για παράδειγμα, αυτή η εντολή ελέγχει και διορθώνει τον κώδικα στους φακέλους `src` και `tests` στον τρέχοντα κατάλογο: +Τώρα θα πρέπει να μπορείτε να εκτελέσετε το εργαλείο στο τερματικό. Η πρώτη εντολή ελέγχει και η δεύτερη διορθώνει τον κώδικα στους φακέλους `src` και `tests` στον τρέχοντα κατάλογο: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Μεταφορά των αλλαγών .[#toc-committing-the-changes] -=================================================== - -Αφού αλλάξετε τον κώδικα, πρέπει να δεσμεύσετε τις αλλαγές σας. Δημιουργήστε περισσότερες δεσμεύσεις, μία για κάθε λογικό βήμα. Κάθε δέσμευση θα πρέπει να είναι χρησιμοποιήσιμη ως έχει - χωρίς άλλες δεσμεύσεις. Έτσι, οι κατάλληλες δοκιμές θα πρέπει επίσης να περιλαμβάνονται στο ίδιο commit. - -Παρακαλούμε, ελέγξτε δύο φορές ότι ο κώδικάς σας ταιριάζει με τους κανόνες: -- Ο κώδικας δεν παράγει σφάλματα -- Ο κώδικάς σας δεν παραβιάζει καμία δοκιμή. -- Η αλλαγή του κώδικά σας είναι δοκιμασμένη. -- Δεν διαπράττετε άχρηστες αλλαγές λευκού χώρου. +Περιγραφή δέσμευσης .[#toc-commit-description] +============================================== -Το μήνυμα δέσμευσης πρέπει να ακολουθεί τη μορφή `Latte: fixed multi template rendering [Closes # 69]` δηλ: -- μια περιοχή ακολουθούμενη από μια άνω και κάτω τελεία -- ο σκοπός της δέσμευσης στο παρελθόν, αν είναι δυνατόν, ξεκινήστε με "added.", "fixed.", "refactored.", changed, removed -- ενδεχόμενος σύνδεσμος προς τον ιχνηλάτη ζητημάτων -- αν η δέσμευση ακυρώνει την προς τα πίσω συμβατότητα, προσθέστε "BC break" -- μπορεί να υπάρχει μια ελεύθερη γραμμή μετά το θέμα και μια πιο λεπτομερής περιγραφή, συμπεριλαμβανομένων των συνδέσμων προς το φόρουμ. +Στη Nette, τα θέματα των δεσμεύσεων έχουν την ακόλουθη μορφή: `Presenter: fixed AJAX detection [Closes #69]` +- περιοχή ακολουθούμενη από άνω και κάτω τελεία +- σκοπός της δέσμευσης σε παρελθοντικό χρόνο- αν είναι δυνατόν, αρχίστε με λέξεις όπως: "added .(νέο χαρακτηριστικό)", "fixed .(διόρθωση)", "refactored .(αλλαγή κώδικα χωρίς αλλαγή συμπεριφοράς)", changed, removed +- αν η δέσμευση σπάει την προς τα πίσω συμβατότητα, προσθέστε "BC break" +- οποιαδήποτε σύνδεση με τον ανιχνευτή ζητημάτων, όπως `(#123)` ή `[Closes #69]` +- μετά το θέμα, μπορεί να υπάρχει μία κενή γραμμή, ακολουθούμενη από μία πιο λεπτομερή περιγραφή, συμπεριλαμβανομένων, για παράδειγμα, συνδέσμων προς το φόρουμ -Pull-Requesting των Commits .[#toc-pull-requesting-the-commits] -=============================================================== -Αν είστε ικανοποιημένοι με τις αλλαγές και τις δεσμεύσεις του κώδικά σας, πρέπει να προωθήσετε τις δεσμεύσεις σας στο GitHub. +Περιγραφή αιτήματος μετακίνησης .[#toc-pull-request-description] +================================================================ -```shell -git push origin new_branch_name -``` +Κατά τη δημιουργία ενός pull request, η διεπαφή του GitHub θα σας επιτρέψει να εισαγάγετε έναν τίτλο και μια περιγραφή. Δώστε έναν συνοπτικό τίτλο και συμπεριλάβετε όσο το δυνατόν περισσότερες πληροφορίες στην περιγραφή σχετικά με τους λόγους της αλλαγής σας. -Οι αλλαγές είναι παρούσες δημόσια, ωστόσο, πρέπει να προτείνετε τις αλλαγές σας για ενσωμάτωση στον κύριο κλάδο του Nette. Για να το κάνετε αυτό, [κάντε ένα pull request |https://help.github.com/articles/creating-a-pull-request]. -Κάθε pull request έχει έναν τίτλο και μια περιγραφή. Παρακαλούμε δώστε κάποιον περιγραφικό τίτλο. Συχνά είναι παρόμοιος με το όνομα του κλάδου, για παράδειγμα "Securing signals against CSRF attack". +Επίσης, προσδιορίστε στην επικεφαλίδα αν πρόκειται για νέο χαρακτηριστικό ή διόρθωση σφάλματος και αν μπορεί να προκαλέσει προβλήματα συμβατότητας προς τα πίσω (BC break). Αν υπάρχει σχετικό θέμα, παραπέμψτε σε αυτό, ώστε να κλείσει με την έγκριση του pull request. -Η περιγραφή του pull request θα πρέπει να περιέχει κάποιες πιο συγκεκριμένες πληροφορίες σχετικά με τις αλλαγές στον κώδικά σας: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Παρακαλούμε αλλάξτε τον πίνακα πληροφοριών ώστε να ταιριάζει στο pull request σας. Σχόλια σε κάθε στοιχείο του καταλόγου: -- Λέει αν το pull request προσθέτει **feature** ή είναι **bugfix**. -- Παραπέμπει τελικά σε **σχετικό θέμα**, το οποίο θα κλείσει μετά τη συγχώνευση του pull request. -- Λέει αν το pull request χρειάζεται τις **αλλαγές τεκμηρίωσης**, αν ναι, παρέχετε παραπομπές στα κατάλληλα pull requests. Δεν χρειάζεται να παρέχετε αμέσως την αλλαγή τεκμηρίωσης, ωστόσο, το pull request δεν θα συγχωνευθεί αν η αλλαγή τεκμηρίωσης είναι απαραίτητη. Η αλλαγή τεκμηρίωσης πρέπει να προετοιμαστεί για την αγγλική τεκμηρίωση, οι μεταλλάξεις άλλων γλωσσών είναι προαιρετικές. -- Λέει αν το pull request δημιουργεί **ένα σπάσιμο της BC**. Παρακαλώ, θεωρήστε οτιδήποτε αλλάζει τη δημόσια διεπαφή ως BC break. - -Ο τελικός πίνακας θα μπορούσε να μοιάζει ως εξής: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Επεξεργασία των αλλαγών σας .[#toc-reworking-your-changes] -========================================================== -Είναι πολύ συνηθισμένο να λαμβάνετε σχόλια για την αλλαγή του κώδικά σας. Παρακαλούμε, προσπαθήστε να ακολουθήσετε τις προτεινόμενες αλλαγές και επεξεργαστείτε τις δεσμεύσεις σας για να το κάνετε αυτό. Μπορείτε να δεσμεύσετε τις προτεινόμενες αλλαγές ως νέες δεσμεύσεις και στη συνέχεια να τις συμπιέσετε με τις προηγούμενες. Ανατρέξτε στο κεφάλαιο [Interactive rebase |https://help.github.com/en/github/using-git/about-git-rebase] στο GitHub. Αφού επαναπροσδιορίσετε τις αλλαγές σας, προωθήστε τις αλλαγές σας με δύναμη στην απομακρυσμένη διχάλα σας, όλα θα διαδοθούν αυτόματα στο pull request. {{priority: -1}} diff --git a/contributing/en/code.texy b/contributing/en/code.texy index e769275107..6714b027ad 100644 --- a/contributing/en/code.texy +++ b/contributing/en/code.texy @@ -1,110 +1,118 @@ -Proposing a Change in Code -************************** +Contributing to Code +******************** -Nette Framework uses Git and [GitHub |https://github.com/nette/nette] for maintaining the code base. The best way to contribute is to commit your changes to your own fork and then make a pull request on GitHub. This document summarize the major steps for successful contributing. +.[perex] +Are you planning to contribute to the Nette Framework and need to familiarize yourself with the rules and procedures? This beginner's guide will walk you through the steps to effectively contribute to the code, work with repositories, and implement changes. -Preparing Environment -===================== +Procedure +========= -Start with [forking |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette on GitHub |https://github.com/nette]. Carefully [set up |https://help.github.com/en/github/getting-started-with-github/set-up-git] your local Git environment, configure your username and email, these credentials will identify your changes in Nette Framework history. +To contribute to the code, it is essential to have an account on [GitHub|https://github.com] and be familiar with the basics of working with the Git version control system. If you are not familiar with Git, you can check out the [git - the simple guide|https://rogerdudler.github.io/git-guide/] and consider using one of the many [graphical clients|https://git-scm.com/downloads/guis]. -Working on Your Patch -===================== +Preparing the Environment and Repository +---------------------------------------- -Before you start working on your patch, create a new branch for your changes. -```shell -git checkout -b new_branch_name -``` +1) On GitHub, create a [fork|https://help.github.com/en/github/getting-started-with-github/fork-a-repo] of the [package repository|www:packages] that you intend to modify +2) [Clone|https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] this repository to your computer +3) Install the dependencies, including [Nette Tester|tester:], using the `composer install` command +4) Verify that the tests are working by running `composer tester` +5) Create a [new branch|#New Branch] based on the latest released version + + +Implementing Your Own Changes +----------------------------- + +Now you can make your own code adjustments: + +1) Implement the desired changes and do not forget about the tests +2) Make sure the tests run successfully using `composer tester` +3) Check if the code meets the [#coding standards] +4) Save (commit) the changes with a description in [this format|#Commit Description] + +You can create multiple commits, one for each logical step. Each commit should be meaningful on its own. + + +Submitting Changes +------------------ + +Once you are satisfied with the changes, you can submit them: + +1) Push the changes to GitHub to your fork +2) From there, submit them to the Nette repository by creating a [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Provide [sufficient information|#pull request description] in the description + + +Incorporating Feedback +---------------------- + +Your commits are now visible to others. It is common to receive comments with suggestions: -You can work on your code change. +1) Keep track of the proposed changes +2) Incorporate them as new commits or [merge them with previous ones|https://help.github.com/en/github/using-git/about-git-rebase] +3) Resubmit the commits to GitHub, and they will automatically appear in the pull request -If possible, make changes from the last released version. +Never create a new pull request to modify an existing one. -Testing Your Changes -==================== +Documentation +------------- -You need to install Nette Tester. The easiest way is to call `composer install` in repository root. Now you should be able to run tests with `./vendor/bin/tester` in the terminal. +If you have changed functionality or added a new one, don't forget to [add it to the documentation|documentation] as well. -Some tests may fail due to missing php.ini. Therefore you should call the runner with parameter -c and specify the path to php.ini, for example `./vendor/bin/tester -c ./tests/php.ini`. -After you are able to run the tests, you can implement your own or change the failing to match the new behavior. Read more about testing with Nette Tester in [documentation page |tester:]. +New Branch +========== + +If possible, make changes against the latest released version, i.e., the last tag in the branch. For the tag v3.2.1, create a branch using this command: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Coding Standards ================ -Your code must follow [coding standard] used in Nette Framework. It is easy because there is automated checker & fixer. It can be installed via Composer to your chosen global directory: +Your code must meet the [coding standard] used in the Nette Framework. There is an automatic tool available for checking and fixing the code. You can install it **globally** through Composer to a folder of your choice: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Now you should be able to run tool in the terminal. For example, this command checks and fixes code in folders `src` and `tests` in current directory: +Now you should be able to run the tool in the terminal. The first command checks and the second one fixes the code in the `src` and `tests` folders in the current directory: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Committing the Changes -====================== +Commit Description +================== -After you have changed the code, you have to commit your changes. Create more commits, one for each logical step. Each commit should have been usable as is - without other commits. So, the appropriate tests should be also included in the same commit. +In Nette, commit subjects have the following format: `Presenter: fixed AJAX detection [Closes #69]` -Please, double-check your code fits the rules: -- Code does not generate any errors -- Your code does not break any tests. -- Your code change is tested. -- You are not committing useless white-space changes. +- area followed by a colon +- purpose of the commit in the past tense; if possible, start with words like: "added .(new feature)", "fixed .(correction)", "refactored .(code change without behavior change)", changed, removed +- if the commit breaks backward compatibility, add "BC break" +- any connection to the issue tracker, such as `(#123)` or `[Closes #69]` +- after the subject, there can be one blank line followed by a more detailed description, including, for example, links to the forum -Commit message should follow format `Latte: fixed multi template rendering [Closes # 69]` ie: -- an area followed by a colon -- the purpose of the commit in the past, if possible, start with "added.", "fixed.", "refactored.", changed, removed -- eventual link to issue tracker -- if commit cancels backward compatibility, add "BC break" -- there may be one free line after the subject and a more detailed description including links to the forum. +Pull Request Description +======================== -Pull-Requesting the Commits -=========================== - -If you are satisfied with your code changes & commits, you have to push you commits to GitHub. - -```shell -git push origin new_branch_name -``` +When creating a pull request, the GitHub interface will allow you to enter a title and description. Provide a concise title and include as much information as possible in the description about the reasons for your change. -Changes are present publicly, however, you have to propose your changes for integration into master branch of Nette. To do that, [make a pull request |https://help.github.com/articles/creating-a-pull-request]. -Each pull request has a title and a description. Please provide some describing title. It's often similar to the branch name, for example "Securing signals against CSRF attack." +Also, specify in the header whether it is a new feature or a bug fix and whether it may cause backward compatibility issues (BC break). If there is a related issue, link to it so that it will be closed upon approval of the pull request. -Pull request description should have contain some more specific information about your code changes: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Please change the information table to fit your pull request. Comments to each list item: -- Says if the pull request adds **feature** or it is a **bugfix**. -- References eventually **related issue**, which will be closed after merging the pull request. -- Says if the pull request needs the **documentation changes**, if yes, provide references to the appropriate pull requests. You don't have to provide the documentation change immediately, however, the pull request won't be merged if the documentation change is needed. The documentation change must be prepared for English documentation, other language mutations are optional. -- Says if the pull request creates **a BC break**. Please, consider everything which changes public interface as a BC break. - -The final table could look like: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Reworking Your Changes -====================== -It is really common to receive comments to your code change. Please, try to follow proposed changes and rework your commits to do that. You can commit proposed changes as new commits and then squash them to the previous ones. See [Interactive rebase |https://help.github.com/en/github/using-git/about-git-rebase] chapter on GitHub. After rebasing your changes, force-push your changes to your remote fork, everything will be automatically propagate to the pull request. {{priority: -1}} diff --git a/contributing/es/code.texy b/contributing/es/code.texy index bf3088e863..4d35b1187d 100644 --- a/contributing/es/code.texy +++ b/contributing/es/code.texy @@ -1,110 +1,118 @@ -Proponer un cambio de código -**************************** +Contribuir al código +******************** -Nette Framework utiliza Git y [GitHub |https://github.com/nette/nette] para mantener el código base. La mejor manera de contribuir es confirmar los cambios en tu propio fork y luego hacer un pull request en GitHub. Este documento resume los principales pasos para contribuir con éxito. +.[perex] +¿Está pensando en contribuir al marco de Nette y necesita familiarizarse con las normas y procedimientos? Esta guía para principiantes le guiará a través de los pasos necesarios para contribuir eficazmente al código, trabajar con repositorios e implementar cambios. -Preparación del entorno .[#toc-preparing-environment] -===================================================== +Procedimiento .[#toc-procedure] +=============================== -Comience con la [bifurcación |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] de [Nette en GitHub |https://github.com/nette]. [Configure |https://help.github.com/en/github/getting-started-with-github/set-up-git] cuidadosamente su entorno Git local, configure su nombre de usuario y correo electrónico, estas credenciales identificarán sus cambios en el historial de Nette Framework. +Para contribuir al código, es esencial tener una cuenta en [GitHub |https://github.com] y estar familiarizado con los conceptos básicos de trabajo con el sistema de control de versiones Git. Si no estás familiarizado con Git, puedes consultar la [guía git - the simple guide |https://rogerdudler.github.io/git-guide/] y considerar el uso de uno de los muchos [clientes gráficos |https://git-scm.com/downloads/guis]. -Trabajar en su parche .[#toc-working-on-your-patch] -=================================================== +Preparar el entorno y el repositorio .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------------- -Antes de empezar a trabajar en su parche, cree una nueva rama para sus cambios. -```shell -git checkout -b new_branch_name -``` +1) En GitHub, crea un [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] del repositorio del [paquete |www:packages] que pretendes modificar +2) [Clone |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] este repositorio en su ordenador +3) Instale las dependencias, incluyendo [Nette Tester |tester:], utilizando el comando `composer install` +4) Compruebe que las pruebas funcionan ejecutando `composer tester` +5) Cree una [nueva rama |#New Branch] basada en la última versión publicada -Usted puede trabajar en su cambio de código. -Si es posible, realice los cambios desde la última versión publicada. +Implementar tus propios cambios .[#toc-implementing-your-own-changes] +--------------------------------------------------------------------- +Ahora puede realizar sus propios ajustes en el código: -Pruebe sus cambios .[#toc-testing-your-changes] -=============================================== +1) Implementa los cambios deseados y no te olvides de las pruebas +2) Asegúrate de que las pruebas se ejecutan correctamente `composer tester` +3) Comprueba si el código cumple las [normas de codificación |#coding standards] +4) Guarda (confirma) los cambios con una descripción en [este formato |#Commit Description] + +Puedes crear varios commits, uno para cada paso lógico. Cada commit debe tener sentido por sí mismo. + + +Envío de cambios .[#toc-submitting-changes] +------------------------------------------- + +Una vez que esté satisfecho con los cambios, puede enviarlos: + +1) Empuje los cambios a GitHub a su bifurcación. +2) Desde allí, envíalos al repositorio de Nette creando un [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Proporcione [información suficiente |#pull request description] en la descripción + + +Incorporación de comentarios .[#toc-incorporating-feedback] +----------------------------------------------------------- + +Tus commits son ahora visibles para los demás. Es habitual recibir comentarios con sugerencias: + +1) Hacer un seguimiento de los cambios propuestos +2) Incorpórelos como nuevos commits o [fusiónelos con los |https://help.github.com/en/github/using-git/about-git-rebase]anteriores +3) Vuelve a enviar los commits a GitHub, y aparecerán automáticamente en el pull request + +Nunca crees un nuevo pull request para modificar uno ya existente. + + +Documentación .[#toc-documentation] +----------------------------------- -Necesitas instalar Nette Tester. La forma más fácil es llamar a `composer install` en la raíz del repositorio. Ahora debería ser capaz de ejecutar pruebas con `./vendor/bin/tester` en el terminal. +Si has cambiado alguna funcionalidad o añadido una nueva, no olvides [añadirla también a la |documentation] documentación. -Algunas pruebas pueden fallar debido a la falta de php.ini. Por lo tanto debe llamar al ejecutor con el parámetro -c y especificar la ruta a php.ini, por ejemplo `./vendor/bin/tester -c ./tests/php.ini`. -Después de que pueda ejecutar las pruebas, puede implementar las suyas propias o cambiar el fallo para que coincida con el nuevo comportamiento. Lea más sobre las pruebas con Nette Tester en la [página de documentación |tester:]. +Nueva rama .[#toc-new-branch] +============================= + +Si es posible, realice los cambios contra la última versión publicada, es decir, la última etiqueta de la rama. Para la etiqueta v3.2.1, cree una rama utilizando este comando: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Normas de codificación .[#toc-coding-standards] =============================================== -Tu código debe seguir [los estándares |coding standard] de codificación utilizados en Nette Framework. Es fácil porque hay un comprobador y corrector automático. Se puede instalar a través de Composer a su directorio global elegido: +Tu código debe cumplir las [normas |coding standard] de codificación utilizadas en Nette Framework. Existe una herramienta automática para comprobar y corregir el código. Puedes instalarla **globalmente** a través de Composer en una carpeta de tu elección: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Ahora debería ser capaz de ejecutar la herramienta en el terminal. Por ejemplo, este comando comprueba y corrige el código en las carpetas `src` y `tests` en el directorio actual: +Ahora deberías poder ejecutar la herramienta en el terminal. El primer comando comprueba y el segundo corrige el código en las carpetas `src` y `tests` en el directorio actual: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Confirmación de los cambios .[#toc-committing-the-changes] -========================================================== - -Después de haber cambiado el código, tienes que confirmar los cambios. Crea más confirmaciones, una para cada paso lógico. Cada commit debe ser utilizable tal cual - sin otros commits. Por lo tanto, las pruebas apropiadas también deben incluirse en el mismo commit. +Descripción del compromiso .[#toc-commit-description] +===================================================== -Por favor, compruebe que su código se ajusta a las reglas: -- El código no genera errores -- Su código no rompe ninguna prueba. -- El cambio de código se ha probado. -- No está realizando cambios inútiles en el espacio en blanco. +En Nette, los asuntos de commit tienen el siguiente formato: `Presenter: fixed AJAX detection [Closes #69]` -El mensaje de confirmación debe seguir el formato `Latte: fixed multi template rendering [Closes # 69]` es decir -- un área seguida de dos puntos -- el propósito del commit en el pasado, si es posible, empezar con "añadido", "corregido", "refactorizado", cambiado, eliminado -- eventual enlace al issue tracker -- si la confirmación anula la compatibilidad con versiones anteriores, añada "BC break". -- puede haber una línea libre después del asunto y una descripción más detallada que incluya enlaces al foro. +- área seguida de dos puntos +- propósito del commit en pasado; si es posible, comience con palabras como: added, fixed, refactored, changed, removed +- si la confirmación rompe la compatibilidad con versiones anteriores, añada "BC break". +- cualquier conexión con el gestor de incidencias, como `(#123)` o `[Closes #69]` +- después del asunto, puede haber una línea en blanco seguida de una descripción más detallada, incluyendo, por ejemplo, enlaces al foro -Solicitud de los commits .[#toc-pull-requesting-the-commits] +Descripción de la solicitud .[#toc-pull-request-description] ============================================================ -Si estás satisfecho con los cambios y commits de tu código, tienes que empujar tus commits a GitHub. +Al crear una pull request, la interfaz de GitHub te permitirá introducir un título y una descripción. Proporcione un título conciso e incluya tanta información como sea posible en la descripción sobre las razones de su cambio. -```shell -git push origin new_branch_name -``` +Especifica también en la cabecera si se trata de una nueva función o de una corrección de errores y si puede causar problemas de retrocompatibilidad (BC break). Si existe una incidencia relacionada, vincúlela para que se cierre cuando se apruebe la solicitud de extracción. -Los cambios se presentan públicamente, sin embargo, usted tiene que proponer sus cambios para la integración en la rama maestra de Nette. Para ello, [haga un pull request |https://help.github.com/articles/creating-a-pull-request]. -Cada pull request tiene un título y una descripción. Por favor, proporcione un título descriptivo. A menudo es similar al nombre de la rama, por ejemplo "Securing signals against CSRF attack". - -La descripción del pull request debe contener información más específica sobre los cambios en el código: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Por favor, cambie la tabla de información para adaptarse a su pull request. Comentarios a cada elemento de la lista: -- Indica si el pull request añade **función** o es una **corrección de errores**. -- Eventualmente hace referencia a un **problema relacionado**, que se cerrará tras fusionar la pull request. -- Dice si el pull request necesita **cambios en la documentación**, en caso afirmativo, proporciona referencias a los pull requests apropiados. No tienes que proporcionar el cambio de documentación inmediatamente, sin embargo, el pull request no se fusionará si el cambio de documentación es necesario. El cambio de documentación debe prepararse para la documentación en inglés, las mutaciones en otros idiomas son opcionales. -- Dice si el pull request crea **una ruptura de BC**. Por favor, considere todo lo que cambie la interfaz pública como una ruptura de BC. - -La tabla final podría tener este aspecto: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Reformulando sus cambios .[#toc-reworking-your-changes] -======================================================= -Es muy común recibir comentarios a tus cambios de código. Por favor, intenta seguir los cambios propuestos y reelabora tus commits para ello. Puedes confirmar los cambios propuestos como nuevos commits y luego aplastarlos a los anteriores. Consulta el capítulo [Rebase interactivo |https://help.github.com/en/github/using-git/about-git-rebase] en GitHub. Después de rebase de sus cambios, la fuerza de empuje de sus cambios a su bifurcación remota, todo se propagará automáticamente a la solicitud de extracción. {{priority: -1}} diff --git a/contributing/fr/code.texy b/contributing/fr/code.texy index 84cded40e7..16e2c5f41e 100644 --- a/contributing/fr/code.texy +++ b/contributing/fr/code.texy @@ -1,110 +1,118 @@ -Proposition de modification du code -*********************************** +Contribuer au code +****************** -Nette Framework utilise Git et [GitHub |https://github.com/nette/nette] pour maintenir la base de code. La meilleure façon de contribuer est de commettre vos changements dans votre propre fork et ensuite de faire une demande de pull sur GitHub. Ce document résume les principales étapes d'une contribution réussie. +.[perex] +Vous envisagez de contribuer au Nette Framework et avez besoin de vous familiariser avec les règles et les procédures ? Ce guide du débutant vous guidera à travers les étapes pour contribuer efficacement au code, travailler avec les dépôts et mettre en œuvre les changements. -Préparation de l'environnement .[#toc-preparing-environment] -============================================================ +Procédure .[#toc-procedure] +=========================== -Commencez par [forker |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette sur GitHub |https://github.com/nette]. [Configurez |https://help.github.com/en/github/getting-started-with-github/set-up-git] soigneusement votre environnement Git local, configurez votre nom d'utilisateur et votre adresse e-mail, ces identifiants permettront d'identifier vos modifications dans l'historique du Framework Nette. +Pour contribuer au code, il est essentiel d'avoir un compte sur [GitHub |https://github.com] et d'être familiarisé avec les bases du système de contrôle de version Git. Si vous n'êtes pas familier avec Git, vous pouvez consulter [git - the simple guide |https://rogerdudler.github.io/git-guide/] et envisager d'utiliser l'un des nombreux [clients graphiques |https://git-scm.com/downloads/guis]. -Travailler sur votre correctif .[#toc-working-on-your-patch] -============================================================ +Préparation de l'environnement et du dépôt .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------------------- -Avant de commencer à travailler sur votre patch, créez une nouvelle branche pour vos modifications. -```shell -git checkout -b new_branch_name -``` +1) Sur GitHub, créez un [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] du [dépôt de paquets |www:packages] que vous avez l'intention de modifier. +2) [Clonez |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] ce dépôt sur votre ordinateur +3) Installez les dépendances, y compris [Nette Tester |tester:], en utilisant la commande `composer install` +4) Vérifiez que les tests fonctionnent en exécutant la commande `composer tester` +5) Créer une [nouvelle branche |#New Branch] basée sur la dernière version publiée. -Vous pouvez travailler sur votre changement de code. -Si possible, effectuez les changements à partir de la dernière version publiée. +Mise en œuvre de vos propres modifications .[#toc-implementing-your-own-changes] +-------------------------------------------------------------------------------- +Vous pouvez maintenant apporter vos propres modifications au code : -Test de vos modifications .[#toc-testing-your-changes] -====================================================== +1) Mettez en œuvre les changements souhaités et n'oubliez pas les tests. +2) Assurez-vous que les tests s'exécutent avec succès en utilisant `composer tester` +3) Vérifier si le code répond aux [normes de codage |#coding standards] +4) Sauvegarder (commit) les changements avec une description dans [ce format |#Commit Description] + +Vous pouvez créer plusieurs livraisons, une pour chaque étape logique. Chaque validation doit être significative en soi. + + +Soumettre des modifications .[#toc-submitting-changes] +------------------------------------------------------ + +Une fois que vous êtes satisfait des modifications, vous pouvez les soumettre : + +1) Pousser les changements sur GitHub vers votre fork +2) De là, soumettez-les au dépôt Nette en créant une [pull request|https://help.github.com/articles/creating-a-pull-request] (PR). +3) Fournissez [suffisamment d'informations |#pull request description] dans la description + + +Incorporer le retour d'information .[#toc-incorporating-feedback] +----------------------------------------------------------------- + +Vos modifications sont maintenant visibles par les autres. Il est courant de recevoir des commentaires contenant des suggestions : + +1) Garder une trace des changements proposés +2) Incorporez-les en tant que nouveaux commits ou [fusionnez-les avec les précédents |https://help.github.com/en/github/using-git/about-git-rebase] +3) Resoumettez les commits à GitHub, et ils apparaîtront automatiquement dans la demande d'extraction. + +Ne créez jamais une nouvelle demande d'extraction pour modifier une demande existante. + + +Documentation .[#toc-documentation] +----------------------------------- -Vous devez installer Nette Tester. Le moyen le plus simple est d'appeler `composer install` à la racine du référentiel. Maintenant vous devriez être capable d'exécuter des tests avec `./vendor/bin/tester` dans le terminal. +Si vous avez modifié une fonctionnalité ou en avez ajouté une nouvelle, n'oubliez pas de l'ajouter également [à la documentation |documentation]. -Certains tests peuvent échouer à cause d'un php.ini manquant. Par conséquent, vous devez appeler le runner avec le paramètre -c et spécifier le chemin vers le php.ini, par exemple `./vendor/bin/tester -c ./tests/php.ini`. -Une fois que vous êtes en mesure d'exécuter les tests, vous pouvez implémenter les vôtres ou modifier l'échec pour correspondre au nouveau comportement. Pour en savoir plus sur les tests avec Nette Tester, consultez la [page de documentation |tester:]. +Nouvelle branche .[#toc-new-branch] +=================================== + +Si possible, effectuez les modifications par rapport à la dernière version publiée, c'est-à-dire la dernière balise de la branche. Pour l'étiquette v3.2.1, créez une branche en utilisant cette commande : + +```shell +git checkout -b new_branch_name v3.2.1 +``` Normes de codage .[#toc-coding-standards] ========================================= -Votre code doit respecter les [normes de codage |coding standard] utilisées dans Nette Framework. C'est facile car il existe un vérificateur et un fixateur automatiques. Il peut être installé via Composer dans le répertoire global de votre choix : +Votre code doit respecter les [normes de codage |coding standard] utilisées dans le Nette Framework. Il existe un outil automatique pour vérifier et corriger le code. Vous pouvez l'installer **globalement** via Composer dans un dossier de votre choix : ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Maintenant vous devriez être capable d'exécuter l'outil dans le terminal. Par exemple, cette commande vérifie et corrige le code dans les dossiers `src` et `tests` dans le répertoire actuel : +Vous devriez maintenant être en mesure de lancer l'outil dans le terminal. La première commande vérifie et la seconde corrige le code dans les dossiers `src` et `tests` dans le répertoire courant : ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Validation des modifications .[#toc-committing-the-changes] -=========================================================== +Description de l'engagement .[#toc-commit-description] +====================================================== -Après avoir modifié le code, vous devez valider vos modifications. Créez plusieurs commits, un pour chaque étape logique. Chaque commit doit être utilisable tel quel - sans autres commits. Ainsi, les tests appropriés doivent également être inclus dans le même commit. +Dans Nette, les sujets de commit ont le format suivant : `Presenter: fixed AJAX detection [Closes #69]` -S'il vous plaît, vérifiez que votre code respecte les règles : -- Le code ne génère pas d'erreurs -- Votre code ne casse aucun test. -- Votre changement de code est testé. -- Vous ne commettez pas de modifications inutiles en espace blanc. +- domaine suivi de deux points +- l'objet du commit au passé ; si possible, commencer par des mots comme : added, fixed, refactored, changed, removed +- si le commit rompt la compatibilité ascendante, ajouter "BC break" (rupture de la compatibilité ascendante) +- toute connexion à l'outil de suivi des problèmes, comme `(#123)` ou `[Closes #69]` +- après le sujet, il peut y avoir une ligne vide suivie d'une description plus détaillée, y compris, par exemple, des liens vers le forum. -Le message de validation doit suivre le format suivant `Latte: fixed multi template rendering [Closes # 69]` c'est-à-dire -- une zone suivie de deux points -- le but du commit dans le passé, si possible, commencez par "ajouté.", "corrigé.", "refactorisé.", changé, supprimé -- lien éventuel vers le suivi des problèmes -- si le commit annule la rétrocompatibilité, ajoutez "BC break". -- il peut y avoir une ligne libre après le sujet et une description plus détaillée incluant des liens vers le forum. +Description de la demande de retrait .[#toc-pull-request-description] +===================================================================== -Pull-Requesting des commits .[#toc-pull-requesting-the-commits] -=============================================================== +Lors de la création d'une demande d'extraction, l'interface GitHub vous permet de saisir un titre et une description. Fournissez un titre concis et incluez autant d'informations que possible dans la description sur les raisons de votre changement. -Si vous êtes satisfait de vos modifications de code et de vos commits, vous devez pousser vos commits vers GitHub. +Précisez également dans l'en-tête s'il s'agit d'une nouvelle fonctionnalité ou d'une correction de bogue et si elle peut entraîner des problèmes de compatibilité ascendante (BC break). S'il existe un problème connexe, créez un lien vers celui-ci afin qu'il soit fermé lors de l'approbation de la demande d'extraction. -```shell -git push origin new_branch_name ``` - -Les changements sont présents publiquement, cependant, vous devez proposer vos changements pour l'intégration dans la branche master de Nette. Pour ce faire, [faites une demande de modification (pull request) |https://help.github.com/articles/creating-a-pull-request]. -Chaque pull request a un titre et une description. Veuillez fournir un titre descriptif. Il est souvent similaire au nom de la branche, par exemple "Securing signals against CSRF attack". - -La description de la demande de téléchargement doit contenir des informations plus spécifiques sur les modifications apportées au code : -``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Veuillez modifier le tableau d'information pour l'adapter à votre demande de retrait. Commentaires pour chaque élément de la liste : -- Indique si la demande ajoute une **fonctionnalité** ou s'il s'agit d'un **bugfix**. -- Fait éventuellement référence à **un problème connexe**, qui sera fermé après la fusion de la demande. -- Indique si la demande nécessite des **modifications de la documentation**, si oui, fournit des références aux demandes pull appropriées. Vous n'êtes pas obligé de fournir la modification de la documentation immédiatement, cependant, la demande pull ne sera pas fusionnée si la modification de la documentation est nécessaire. Le changement de documentation doit être préparé pour la documentation en anglais, les mutations dans d'autres langues sont facultatives. -- Indique si la pull request crée **une rupture de la BC**. S'il vous plaît, considérez tout ce qui change l'interface publique comme une rupture de la CB. - -Le tableau final pourrait ressembler à ceci : +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Retravailler vos modifications .[#toc-reworking-your-changes] -============================================================= -Il est très courant de recevoir des commentaires sur vos modifications de code. Essayez de suivre les changements proposés et retravaillez vos commits pour y parvenir. Vous pouvez commiter les changements proposés en tant que nouveaux commits et ensuite les écraser sur les commits précédents. Voir le chapitre sur le [rebasement interactif |https://help.github.com/en/github/using-git/about-git-rebase] sur GitHub. Après avoir rebasé vos modifications, forcez vos modifications à votre fork distant, tout sera automatiquement propagé à la pull request. {{priority: -1}} diff --git a/contributing/hu/code.texy b/contributing/hu/code.texy index e0825cf876..f18bb335bc 100644 --- a/contributing/hu/code.texy +++ b/contributing/hu/code.texy @@ -1,110 +1,118 @@ -Javaslat a szabályzat módosítására -********************************** +Hozzájárulás a kódhoz +********************* -A Nette Framework a Git-et és a [GitHubot |https://github.com/nette/nette] használja a kódbázis karbantartására. A legjobb módja a hozzájárulásnak, ha a változtatásokat a saját elágazásodban rögzíted, majd a GitHubon pull requestet teszel. Ez a dokumentum összefoglalja a sikeres hozzájárulás főbb lépéseit. +.[perex] +Tervezi, hogy hozzájárul a Nette keretrendszerhez, és meg kell ismerkednie a szabályokkal és eljárásokkal? Ez a kezdő útmutató végigvezet a kódhoz való hatékony hozzájárulás, a tárolókkal való munka és a változtatások megvalósításának lépésein. -A környezet előkészítése .[#toc-preparing-environment] -====================================================== +Eljárás .[#toc-procedure] +========================= -Kezdjük a [Nette |https://github.com/nette] [elágazásával |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [a GitHubon |https://github.com/nette]. Gondosan [állítsa be |https://help.github.com/en/github/getting-started-with-github/set-up-git] a helyi Git környezetét, konfigurálja a felhasználónevét és az e-mail címét, ezek a hitelesítő adatok azonosítani fogják a módosításait a Nette Framework történetében. +A kódhoz való hozzájáruláshoz elengedhetetlen, hogy rendelkezz egy fiókkal a [GitHubon |https://github.com], és ismerd a Git verziókezelő rendszerrel való munka alapjait. Ha nem ismered a Git-et, akkor nézd meg a [git - az egyszerű útmutatót |https://rogerdudler.github.io/git-guide/], és fontold meg a számos [grafikus kliens |https://git-scm.com/downloads/guis] egyikének használatát. -A javításon való munka .[#toc-working-on-your-patch] -==================================================== +A környezet és a tároló előkészítése .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------------- -Mielőtt elkezdenél dolgozni a javításodon, hozz létre egy új ágat a változtatásaidnak. -```shell -git checkout -b new_branch_name -``` +1) A GitHubon hozzon létre egy [elágazást |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] a [csomagtárolóból |www:packages], amelyet módosítani kíván. +2) [Klónozzuk |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] ezt a tárolót a számítógépünkre. +3) Telepítse a függőségeket, beleértve a [Nette Tester-t |tester:] is, a `composer install` parancs segítségével. +4) Ellenőrizze, hogy a tesztek működnek-e a következő futtatással `composer tester` +5) Hozzon létre egy [új ágat |#New Branch] a legfrissebb kiadott verzió alapján. -Dolgozhatsz a kódváltoztatásodon. -Ha lehetséges, végezze el a változtatásokat a legutóbb kiadott verzióhoz képest. +Saját változtatások végrehajtása .[#toc-implementing-your-own-changes] +---------------------------------------------------------------------- +Most már elvégezheti saját kódjainak módosítását: -A változtatások tesztelése .[#toc-testing-your-changes] -======================================================= +1) Végezze el a kívánt változtatásokat, és ne feledkezzen meg a tesztekről. +2) Győződjön meg róla, hogy a tesztek sikeresen futnak a `composer tester` +3) Ellenőrizze, hogy a kód megfelel-e a [kódolási szabványoknak |#coding standards]. +4) Mentse (commit) a változtatásokat egy leírással [ebben a formátumban |#Commit Description] + +Több commitot is létrehozhat, egyet-egyet minden egyes logikai lépéshez. Minden commitnak önmagában is értelmesnek kell lennie. + + +Változások elküldése .[#toc-submitting-changes] +----------------------------------------------- + +Ha elégedett a módosításokkal, elküldheti azokat: + +1) Tolja a változtatásokat a GitHubra a saját elágazásához +2) Onnan küldje el őket a Nette tárolóba egy [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) létrehozásával. +3) Adjon meg [elegendő információt |#pull request description] a leírásban + + +Visszajelzések beépítése .[#toc-incorporating-feedback] +------------------------------------------------------- + +A commitjaid most már mások számára is láthatóak. Gyakori, hogy javaslatokat tartalmazó megjegyzéseket kapunk: + +1) Tartsa nyomon a javasolt változtatásokat +2) építse be őket új commitként vagy [egyesítse őket a korábbiakkal |https://help.github.com/en/github/using-git/about-git-rebase] +3) Küldje el újra a commitokat a GitHubra, és azok automatikusan megjelennek a pull requestben. + +Soha ne hozzon létre új pull requestet egy meglévő módosításához. + + +Dokumentáció .[#toc-documentation] +---------------------------------- -Telepítenie kell a Nette Testert. A legegyszerűbb, ha a `composer install` címet hívod meg a repository root-ban. Most már képesnek kell lennie a tesztek futtatására a `./vendor/bin/tester` segítségével a terminálban. +Ha megváltoztattál egy funkciót, vagy új funkciót adtál hozzá, ne felejtsd el [azt is hozzáadni a dokumentációhoz |documentation]. -Néhány teszt sikertelen lehet a hiányzó php.ini miatt. Ezért a futót a -c paraméterrel kell hívni, és meg kell adni a php.ini elérési útvonalát, például `./vendor/bin/tester -c ./tests/php.ini`. -Miután a tesztek futtatására képes, megvalósíthatja a saját tesztjeit, vagy megváltoztathatja a hibás működést, hogy megfeleljen az új viselkedésnek. A Nette Testerrel való tesztelésről bővebben a [dokumentációs oldalon |tester:] olvashat. +Új ág .[#toc-new-branch] +======================== + +Ha lehetséges, végezze el a változtatásokat a legutolsó kiadott verzióval, azaz az ág utolsó tagjével szemben. A v3.2.1 címkéhez hozzon létre egy ágat ezzel a paranccsal: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Kódolási szabványok .[#toc-coding-standards] ============================================ -A kódnak követnie kell a Nette Frameworkben használt [kódolási szabványt |coding standard]. Ez könnyű, mert van automatikus ellenőrző és javító program. A Composer segítségével telepíthető a kiválasztott globális könyvtárba: +A kódnak meg kell felelnie a Nette Frameworkben használt [kódolási szabványnak |coding standard]. A kód ellenőrzésére és javítására automatikus eszköz áll rendelkezésre. Ezt **globálisan** telepítheted a Composer segítségével egy általad választott mappába: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Most már képesnek kell lennie arra, hogy futtassa az eszközt a terminálban. Például ez a parancs ellenőrzi és javítja a kódot az aktuális könyvtár `src` és `tests` mappáiban: +Most már képesnek kell lennie az eszköz futtatására a terminálban. Az első parancs ellenőrzi, a második pedig javítja a kódot az aktuális könyvtárban lévő `src` és `tests` mappákban: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -A változtatások átadása .[#toc-committing-the-changes] -====================================================== +Kötelezettségvállalás Leírás .[#toc-commit-description] +======================================================= -Miután megváltoztattad a kódot, a változtatásokat rögzítened kell. Hozzon létre több commitot, egyet-egyet minden logikai lépéshez. Minden commitnak önmagában - más commitok nélkül - használhatónak kell lennie. Tehát a megfelelő teszteket is ugyanabban a commitban kell szerepeltetni. +A Nette-ben a commit témák a következő formátumúak: `Presenter: fixed AJAX detection [Closes #69]` -Kérjük, ellenőrizd kétszer is, hogy a kódod megfelel-e a szabályoknak: -- A kód nem generál hibát -- A kódod nem törik meg egyetlen tesztet sem. -- A kódváltoztatásod tesztelve van. -- Nem követsz el haszontalan white-space változtatásokat. +- terület, amelyet kettőspont követ +- a kötelezettségvállalás célja múlt időben; ha lehetséges, kezdje a következő szavakkal: added, fixed, refactored, changed, removed +- ha a commit megszakítja a visszafelé kompatibilitást, írja be, hogy "BC break". +- bármilyen kapcsolat a hibakövetővel, például `(#123)` vagy `[Closes #69]` +- a tárgy után egy üres sor következhet, amelyet egy részletesebb leírás követhet, beleértve például a fórumra mutató linkeket is. -A Commit üzenetnek a következő formátumot kell követnie `Latte: fixed multi template rendering [Closes # 69]` azaz: -- egy terület, amelyet kettőspont követ -- a commit célja a múltban, ha lehetséges, kezdje "added.", "fixed.", "refactored.", changed, removed -- esetleges link a hibakövetésre -- ha a commit visszaveti a visszafelé kompatibilitást, hozzá kell adni a "BC break" szót. -- a tárgy után lehet egy szabad sor és egy részletesebb leírás, beleértve a fórumra mutató linkeket is. +Pull Request leírása .[#toc-pull-request-description] +===================================================== -A commitok kihúzása-kérése .[#toc-pull-requesting-the-commits] -============================================================== +A GitHub felületén a pull request létrehozásakor megadhat egy címet és egy leírást. Adjon tömör címet, és a leírásban adjon meg minél több információt a változtatás okairól. -Ha elégedett vagy a kódváltoztatásokkal és commitokkal, akkor a commitokat a GitHub-ra kell tolnod. +A fejlécben azt is adja meg, hogy új funkcióról vagy hibajavításról van-e szó, és hogy okozhat-e visszafelé kompatibilitási problémákat (BC break). Ha van kapcsolódó probléma, hivatkozzon rá, hogy az a pull request jóváhagyásakor lezárásra kerüljön. -```shell -git push origin new_branch_name ``` - -A változtatások nyilvánosan jelen vannak, azonban a Nette master ágába való integrálásra fel kell ajánlanod a változtatásokat. Ehhez [tegyen egy pull requestet |https://help.github.com/articles/creating-a-pull-request]. -Minden pull requestnek van egy címe és egy leírása. Kérjük, adjon meg valamilyen leíró címet. Ez gyakran hasonlít az ág nevéhez, például "Securing signals against CSRF attack". - -A pull request leírásának tartalmaznia kell néhány konkrétabb információt a kódváltoztatásokról: -``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Kérjük, módosítsa az információs táblázatot a pull request-nek megfelelően. Megjegyzések az egyes listaelemekhez: -- Megadja, hogy a pull request **feature**-t ad hozzá, vagy **bugfix**. -- Hivatkozás az esetleges **kapcsolódó problémára**, amely a pull request egyesítése után lezárásra kerül. -- Megmondja, hogy a pull requestnek szüksége van-e **dokumentációs változtatásokra**, ha igen, adjon meg hivatkozásokat a megfelelő pull requestekre. Nem kell azonnal megadni a dokumentációs változtatást, azonban a pull request nem lesz egyesítve, ha a dokumentációs változtatásra szükség van. A dokumentációváltozást az angol nyelvű dokumentációhoz kell elkészíteni, más nyelvi mutációk nem kötelezőek. -- Jelzi, ha a pull request **a BC törést** hoz létre. Kérjük, tekintsen BC breaknek mindent, ami a nyilvános interfészt megváltoztatja. - -A végső táblázat így nézhet ki: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -A változtatások átdolgozása .[#toc-reworking-your-changes] -========================================================== -Nagyon gyakori, hogy megjegyzéseket kapunk a kódváltoztatásunkhoz. Kérjük, próbáld meg követni a javasolt változtatásokat, és ennek megfelelően dolgozzátok át a commitokat. A javasolt változtatásokat új commit-ként commit-olhatja, majd összenyomhatja a korábbiakkal. Lásd az [Interaktív újraközlés |https://help.github.com/en/github/using-git/about-git-rebase] fejezetet a GitHubon. A változtatások újraalapozása után force-push a változtatásokat a távoli elágazásodra, minden automatikusan átkerül a pull requestbe. {{priority: -1}} diff --git a/contributing/it/code.texy b/contributing/it/code.texy index c42e7e8ce0..1e1cb654a4 100644 --- a/contributing/it/code.texy +++ b/contributing/it/code.texy @@ -1,110 +1,118 @@ -Proposta di modifica del codice -******************************* +Contribuire al codice +********************* -Nette Framework utilizza Git e [GitHub |https://github.com/nette/nette] per la manutenzione del codice base. Il modo migliore per contribuire è fare il commit delle modifiche al proprio fork e poi fare una richiesta di pull su GitHub. Questo documento riassume i passi principali per contribuire con successo. +.[perex] +Avete intenzione di contribuire al framework Nette e avete bisogno di familiarizzare con le regole e le procedure? Questa guida per principianti vi guiderà attraverso i passaggi per contribuire efficacemente al codice, lavorare con i repository e implementare le modifiche. -Preparazione dell'ambiente .[#toc-preparing-environment] -======================================================== +Procedura .[#toc-procedure] +=========================== -Iniziare con il [fork di |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette su GitHub |https://github.com/nette]. [Impostate |https://help.github.com/en/github/getting-started-with-github/set-up-git] con cura il vostro ambiente Git locale, configurate il vostro nome utente e la vostra e-mail; queste credenziali identificheranno le vostre modifiche nella cronologia del framework Nette. +Per contribuire al codice, è essenziale avere un account su [GitHub |https://github.com] e conoscere le basi del sistema di controllo di versione Git. Se non si ha familiarità con Git, si può consultare la [guida git - the simple guide |https://rogerdudler.github.io/git-guide/] e prendere in considerazione l'utilizzo di uno dei tanti [client grafici |https://git-scm.com/downloads/guis]. -Lavorare sulla patch .[#toc-working-on-your-patch] -================================================== +Preparazione dell'ambiente e del repository .[#toc-preparing-the-environment-and-repository] +-------------------------------------------------------------------------------------------- -Prima di iniziare a lavorare sulla patch, create un nuovo ramo per le vostre modifiche. -```shell -git checkout -b new_branch_name -``` +1) Su GitHub, creare un [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] del [repository del pacchetto |www:packages] che si intende modificare +2) [Clonare |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] questo repository sul proprio computer +3) Installare le dipendenze, compreso [Nette Tester |tester:], usando il comando `composer install`. +4) Verificate che i test funzionino eseguendo `composer tester` +5) Creare un [nuovo ramo |#New Branch] basato sull'ultima versione rilasciata + + +Implementare le proprie modifiche .[#toc-implementing-your-own-changes] +----------------------------------------------------------------------- + +Ora è possibile apportare le proprie modifiche al codice: + +1) Implementare le modifiche desiderate e non dimenticare i test. +2) Assicurarsi che i test vengano eseguiti con successo `composer tester` +3) Verificare se il codice è conforme agli standard di [codifica |#coding standards] +4) Salvare (commit) le modifiche con una descrizione in [questo formato |#Commit Description] + +È possibile creare più commit, uno per ogni fase logica. Ogni commit deve essere significativo di per sé. + + +Invio delle modifiche .[#toc-submitting-changes] +------------------------------------------------ + +Una volta soddisfatti delle modifiche, è possibile inviarle: + +1) Spingere le modifiche su GitHub al proprio fork +2) Da lì, inviarle al repository di Nette creando una [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Fornire [informazioni sufficienti |#pull request description] nella descrizione + + +Incorporare il feedback .[#toc-incorporating-feedback] +------------------------------------------------------ + +I vostri commit sono ora visibili agli altri. È frequente ricevere commenti con suggerimenti: -È possibile lavorare sulla modifica del codice. +1) Tenere traccia delle modifiche proposte +2) incorporarle come nuovi commit o [unirle a quelle precedenti |https://help.github.com/en/github/using-git/about-git-rebase] +3) Ripresentare i commit a GitHub, che appariranno automaticamente nella richiesta di pull. -Se possibile, apportate le modifiche dall'ultima versione rilasciata. +Non creare mai una nuova richiesta di pull per modificarne una esistente. -Testare le modifiche .[#toc-testing-your-changes] -================================================= +Documentazione .[#toc-documentation] +------------------------------------ -È necessario installare Nette Tester. Il modo più semplice è chiamare `composer install` nella root del repository. Ora si dovrebbe essere in grado di eseguire i test con `./vendor/bin/tester` nel terminale. +Se avete modificato una funzionalità o ne avete aggiunta una nuova, non dimenticate di [aggiungerla |documentation] anche [alla documentazione |documentation]. -Alcuni test potrebbero fallire a causa della mancanza di php.ini. Pertanto, si dovrebbe chiamare il runner con il parametro -c e specificare il percorso di php.ini, per esempio `./vendor/bin/tester -c ./tests/php.ini`. -Dopo aver eseguito i test, è possibile implementare i propri o modificare i fallimenti per adattarli al nuovo comportamento. Per saperne di più sui test con Nette Tester, consultare la [pagina della documentazione |tester:]. +Nuovo ramo .[#toc-new-branch] +============================= + +Se possibile, apportare le modifiche all'ultima versione rilasciata, cioè all'ultimo tag del ramo. Per il tag v3.2.1, creare un ramo usando questo comando: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Standard di codifica .[#toc-coding-standards] ============================================= -Il codice deve seguire gli [standard di codifica |coding standard] utilizzati in Nette Framework. È facile, perché c'è un programma di controllo e correzione automatico. Può essere installato tramite Composer nella cartella globale scelta: +Il codice deve essere conforme agli [standard di codifica |coding standard] utilizzati da Nette Framework. È disponibile uno strumento automatico per la verifica e la correzione del codice. È possibile installarlo **globalmente** tramite Composer in una cartella a scelta: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Ora si dovrebbe essere in grado di eseguire lo strumento nel terminale. Ad esempio, questo comando controlla e corregge il codice nelle cartelle `src` e `tests` della directory corrente: +Ora dovreste essere in grado di eseguire lo strumento nel terminale. Il primo comando controlla e il secondo corregge il codice nelle cartelle `src` e `tests` nella directory corrente: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Applica le modifiche .[#toc-committing-the-changes] +Descrizione dell'impegno .[#toc-commit-description] =================================================== -Dopo aver modificato il codice, è necessario eseguire il commit delle modifiche. Creare più commit, uno per ogni passo logico. Ogni commit deve essere utilizzabile così com'è, senza altri commit. Quindi, anche i test appropriati devono essere inclusi nello stesso commit. +In Nette, gli argomenti dei commit hanno il seguente formato: `Presenter: fixed AJAX detection [Closes #69]` -Verificare che il codice sia conforme alle regole: -- Il codice non genera errori -- Il codice non infrange alcun test. -- La modifica del codice è testata. -- Non state apportando modifiche inutili allo spazio bianco. +- area seguita da due punti +- scopo del commit al passato; se possibile, iniziare con parole come: added, fixed, refactored, changed, removed +- se il commit rompe la compatibilità all'indietro, aggiungere "BC break". +- qualsiasi collegamento al tracker dei problemi, come `(#123)` o `[Closes #69]` +- dopo l'oggetto, può esserci una riga vuota seguita da una descrizione più dettagliata, che includa, per esempio, collegamenti al forum -Il messaggio di commit dovrebbe seguire il formato `Latte: fixed multi template rendering [Closes # 69]` cioè -- un'area seguita da due punti -- lo scopo del commit in passato, se possibile, iniziare con "added.", "fixed.", "refactored.", changed, removed -- eventuale link al tracker dei problemi -- se il commit annulla la retrocompatibilità, aggiungere "BC break". -- può esserci una riga libera dopo l'oggetto e una descrizione più dettagliata, compresi i link al forum. +Descrizione della richiesta .[#toc-pull-request-description] +============================================================ -Richiedere i commit .[#toc-pull-requesting-the-commits] -======================================================= - -Se si è soddisfatti delle modifiche e dei commit apportati al codice, è necessario inviare i commit a GitHub. - -```shell -git push origin new_branch_name -``` +Quando si crea una richiesta di pull, l'interfaccia di GitHub consente di inserire un titolo e una descrizione. Fornire un titolo conciso e includere nella descrizione il maggior numero possibile di informazioni sulle ragioni della modifica. -Le modifiche sono presenti pubblicamente, tuttavia è necessario proporre le proprie modifiche per l'integrazione nel ramo master di Nette. Per farlo, bisogna fare [una richiesta di pull |https://help.github.com/articles/creating-a-pull-request]. -Ogni richiesta di pull ha un titolo e una descrizione. Si prega di fornire un titolo descrittivo. Spesso è simile al nome del ramo, ad esempio "Messa in sicurezza dei segnali contro gli attacchi CSRF". +Inoltre, specificare nell'intestazione se si tratta di una nuova funzionalità o di una correzione di un bug e se può causare problemi di retrocompatibilità (BC break). Se esiste un problema correlato, collegarlo ad esso in modo che venga chiuso dopo l'approvazione della richiesta di pull. -La descrizione della richiesta di pull dovrebbe contenere alcune informazioni più specifiche sulle modifiche al codice: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Modificare la tabella delle informazioni per adattarla alla richiesta di pull. Commenti a ogni voce dell'elenco: -- Dice se la richiesta di pull aggiunge una **caratteristica** o se è un **bugfix**. -- Riferisce eventualmente un **problema correlato**, che sarà chiuso dopo l'unione della richiesta di pull. -- Dice se la richiesta di pull necessita di **modifiche alla documentazione**, se sì, fornire i riferimenti alle richieste di pull appropriate. Non è necessario fornire immediatamente la modifica della documentazione, tuttavia la richiesta di pull non sarà unita se la modifica della documentazione è necessaria. La modifica della documentazione deve essere preparata per la documentazione in inglese, le altre mutazioni linguistiche sono opzionali. -- Dice se la richiesta di pull crea **una rottura del BC**. Si prega di considerare tutto ciò che modifica l'interfaccia pubblica come un'interruzione del BC. - -La tabella finale potrebbe essere simile a: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Rielaborazione delle modifiche .[#toc-reworking-your-changes] -============================================================= -È molto comune ricevere commenti alle proprie modifiche al codice. Cercate di seguire le modifiche proposte e rielaborate i vostri commit per farlo. È possibile fare il commit delle modifiche proposte come nuovi commit e poi schiacciarli su quelli precedenti. Vedere il capitolo sul [rebase interattivo |https://help.github.com/en/github/using-git/about-git-rebase] su GitHub. Dopo aver effettuato il rebase delle modifiche, spingere con forza le modifiche al proprio fork remoto: tutto verrà automaticamente propagato alla richiesta di pull. {{priority: -1}} diff --git a/contributing/pl/code.texy b/contributing/pl/code.texy index c8f9f08485..6b340f4f81 100644 --- a/contributing/pl/code.texy +++ b/contributing/pl/code.texy @@ -1,110 +1,118 @@ -Proponowanie zmiany w kodeksie -****************************** +Wkład do kodu +************* -Nette Framework używa Git i [GitHub |https://github.com/nette/nette] do utrzymania bazy kodu. Najlepszym sposobem na wniesienie wkładu w kod jest commitowanie swoich zmian do własnego forka, a następnie zgłoszenie pull request na GitHubie. Ten dokument podsumowuje główne kroki, które należy podjąć, aby skutecznie przyczynić się do rozwoju kodu. +.[perex] +Planujesz wnieść swój wkład do Nette Framework i potrzebujesz zapoznać się z zasadami i procedurami? Ten przewodnik dla początkujących poprowadzi Cię przez kroki, które pozwolą Ci efektywnie współtworzyć kod, pracować z repozytoriami i wprowadzać zmiany. -Przygotowanie środowiska .[#toc-preparing-environment] -====================================================== +Procedura .[#toc-procedure] +=========================== -Zacznij od [rozwidlenia |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette na GitHubie |https://github.com/nette]. Starannie [skonfiguruj |https://help.github.com/en/github/getting-started-with-github/set-up-git] swoje lokalne środowisko Git, skonfiguruj swoją nazwę użytkownika i e-mail, te poświadczenia będą identyfikować twoje zmiany w historii Nette Framework. +Aby wnieść wkład do kodu, konieczne jest posiadanie konta na [GitHubie |https://github.com] i znajomość podstaw pracy z systemem kontroli wersji Git. Jeśli nie jesteś zaznajomiony z Gitem, możesz sprawdzić [git - prosty przewodnik |https://rogerdudler.github.io/git-guide/] i rozważyć użycie jednego z wielu [graficznych klientów |https://git-scm.com/downloads/guis]. -Praca nad twoją łatką .[#toc-working-on-your-patch] -=================================================== +Przygotowanie środowiska i repozytorium .[#toc-preparing-the-environment-and-repository] +---------------------------------------------------------------------------------------- + +1) Na GitHubie utwórz [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [repozytorium pakietów |www:packages], które zamierzasz zmodyfikować +2) [Sklonuj |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] to repozytorium na swój komputer +3) Zainstaluj zależności, w tym [Nette Tester |tester:], używając polecenia `composer install` +4) Sprawdź, czy testy działają, uruchamiając `composer tester` +5) Utwórz [nową gałąź |#New Branch] opartą na najnowszej wydanej wersji + + +Wdrażanie własnych zmian .[#toc-implementing-your-own-changes] +-------------------------------------------------------------- + +Teraz możesz wprowadzić własne poprawki do kodu: + +1) Zaimplementuj pożądane zmiany i nie zapomnij o testach +2) Upewnij się, że testy przebiegają pomyślnie używając `composer tester` +3) Sprawdź, czy kod spełnia [standardy kodowania |#coding standards] +4) Zapisz (commit) zmiany z opisem w [tym formacie |#Commit Description] + +Możesz stworzyć wiele commitów, po jednym dla każdego logicznego kroku. Każdy commit powinien być znaczący sam w sobie. -Zanim zaczniesz pracować nad swoim patchem, utwórz nową gałąź dla swoich zmian. -```shell -git checkout -b new_branch_name -``` -Możesz pracować nad swoją zmianą kodu. +Przesyłanie zmian .[#toc-submitting-changes] +-------------------------------------------- -Jeśli to możliwe, wprowadź zmiany z ostatniej wydanej wersji. +Gdy zmiany są zadowalające, można je przesłać: +1) Przesuń zmiany na GitHub do swojego forka +2) Stamtąd prześlij je do repozytorium Nette, tworząc [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Podaj [wystarczającą ilość informacji |#pull request description] w opisie -Testowanie zmian .[#toc-testing-your-changes] -============================================= -Musisz zainstalować Nette Tester. Najprostszym sposobem jest wywołanie `composer install` w korzeniu repozytorium. Teraz powinieneś być w stanie uruchomić testy z `./vendor/bin/tester` w terminalu. +Uwzględnianie informacji zwrotnych .[#toc-incorporating-feedback] +----------------------------------------------------------------- -Niektóre testy mogą się nie powieść z powodu braku php.ini. Dlatego należy wywołać runner z parametrem -c i podać ścieżkę do php.ini, na przykład `./vendor/bin/tester -c ./tests/php.ini`. +Twój commit jest teraz widoczny dla innych. Powszechne jest otrzymywanie komentarzy z sugestiami: -Po uruchomieniu testów, możesz zaimplementować własne lub zmienić awarie, aby dopasować je do nowego zachowania. Przeczytaj więcej o testowaniu za pomocą Nette Tester na [stronie dokumentacji |tester:]. +1) Śledzić proponowane zmiany +2) Włącz je jako nowe commity lub [połącz z poprzednimi |https://help.github.com/en/github/using-git/about-git-rebase] +3) Prześlij ponownie commit na GitHub, a automatycznie pojawi się on w żądaniu ściągnięcia. + +Nigdy nie twórz nowego pull requesta, aby zmodyfikować istniejący. + + +Dokumentacja .[#toc-documentation] +---------------------------------- + +Jeśli zmieniłeś funkcjonalność lub dodałeś nową, nie zapomnij [dodać jej |documentation] również [do dokumentacji |documentation]. + + +Nowa gałąź .[#toc-new-branch] +============================= + +Jeśli to możliwe, dokonuj zmian względem najnowszej wydanej wersji, czyli ostatniego tagu w gałęzi. Dla tagu v3.2.1 utwórz gałąź używając tego polecenia: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Standardy kodowania .[#toc-coding-standards] ============================================ -Twój kod musi spełniać [standardy kodowania |coding standard] używane w Nette Framework. Jest to łatwe, ponieważ istnieje automatyczny checker i fixer. Można go zainstalować poprzez Composer do wybranego przez siebie globalnego katalogu: +Twój kod musi spełniać [standardy kodowania |coding standard] stosowane w Nette Framework. Dostępne jest automatyczne narzędzie do sprawdzania i poprawiania kodu. Możesz je zainstalować **globalnie** poprzez Composera do wybranego przez siebie folderu: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Teraz powinieneś być w stanie uruchomić narzędzie w terminalu. Na przykład, to polecenie sprawdza i naprawia kod w folderach `src` i `tests` w bieżącym katalogu: +Teraz powinieneś być w stanie uruchomić narzędzie w terminalu. Pierwsze polecenie sprawdza, a drugie naprawia kod w folderach `src` i `tests` w bieżącym katalogu: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Zobowiązanie do zmiany .[#toc-committing-the-changes] -===================================================== - -Po zmianie kodu, musisz zatwierdzić swoje zmiany. Utwórz więcej commitów, po jednym dla każdego logicznego kroku. Każdy commit powinien nadawać się do użytku jako taki - bez innych commitów. Tak więc, odpowiednie testy powinny być również zawarte w tym samym commicie. +Opis zobowiązania .[#toc-commit-description] +============================================ -Proszę, sprawdź dwukrotnie czy twój kod pasuje do zasad: -- Kod nie generuje żadnych błędów -- Twój kod nie łamie żadnych testów. -- Twoja zmiana kodu jest przetestowana. -- Nie popełniasz bezużytecznych zmian w białej przestrzeni. +W Nette, tematy commitów mają następujący format: `Presenter: fixed AJAX detection [Closes #69]` -Wiadomość o popełnieniu zmiany powinna być zgodna z formatem `Latte: fixed multi template rendering [Closes # 69]` tj: - obszar, po którym następuje dwukropek -- cel commitu w przeszłości, jeśli to możliwe, zacznij od "added.", "fixed.", "refactored.", changed, removed -- ewentualny link do issue tracker -- jeśli commit anuluje kompatybilność wsteczną, dodaj "BC break" -- może być jedna wolna linia po temacie i bardziej szczegółowy opis, w tym linki do forum. - +- cel commitu w czasie przeszłym; jeśli to możliwe, zacznij od słów takich jak: added, fixed, refactored, changed, removed +- jeśli commit łamie wsteczną kompatybilność, dodaj "BC break" +- wszelkie połączenia z issue trackerem, takie jak `(#123)` lub `[Closes #69]` +- po temacie może być jedna pusta linia, po której następuje bardziej szczegółowy opis, zawierający np. linki do forum -Wysyłanie żądania wyciągnięcia .[#toc-pull-requesting-the-commits] -================================================================== -Jeśli jesteś zadowolony ze swoich zmian w kodzie i commitów, musisz wypchnąć swoje commity na GitHub. +Opis Pull Request .[#toc-pull-request-description] +================================================== -```shell -git push origin new_branch_name -``` +Podczas tworzenia pull request, interfejs GitHub pozwoli Ci wprowadzić tytuł i opis. Podaj zwięzły tytuł i zawrzyj jak najwięcej informacji w opisie o powodach zmiany. -Zmiany są prezentowane publicznie, jednak musisz zaproponować swoje zmiany do integracji w gałęzi głównej Nette. Aby to zrobić, należy [złożyć pull request |https://help.github.com/articles/creating-a-pull-request]. -Każdy pull request ma tytuł i opis. Proszę podać jakiś opisujący tytuł. Często jest on podobny do nazwy gałęzi, na przykład "Securing signals against CSRF attack". +Określ również w nagłówku, czy jest to nowa funkcja czy poprawka błędu i czy może spowodować problemy z kompatybilnością wsteczną (BC break). Jeśli istnieje powiązany problem, umieść do niego link, aby został zamknięty po zatwierdzeniu pull requesta. -Opis pull request powinien zawierać jakieś bardziej szczegółowe informacje na temat zmian w Twoim kodzie: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Proszę zmienić tabelę informacji, aby dopasować ją do swojego pull requesta. Komentarze do każdej pozycji listy: -- Mówi czy pull request dodaje **feature** czy jest to **bugfix**. -- Odnosi się ewentualnie do **powiązanego problemu**, który zostanie zamknięty po połączeniu pull requesta. -- Mówi czy pull request wymaga **zmian w dokumentacji**, jeśli tak, podaj referencje do odpowiednich pull requestów. Nie musisz dostarczać zmian w dokumentacji od razu, jednak pull request nie zostanie scalony, jeśli zmiany w dokumentacji są potrzebne. Zmiana dokumentacji musi być przygotowana dla dokumentacji angielskiej, inne mutacje językowe są opcjonalne. -- Mówi, jeśli żądanie pociągnięcia tworzy **przerwę BC**. Proszę, rozważ wszystko, co zmienia publiczny interfejs jako przerwę BC. - -Ostateczna tabela może wyglądać jak: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Przerabianie zmian .[#toc-reworking-your-changes] -================================================= -Otrzymywanie komentarzy do zmian w kodzie jest naprawdę częste. Proszę, spróbuj zastosować się do proponowanych zmian i przerobić swoje commit'y, aby to zrobić. Możesz popełnić proponowane zmiany jako nowe commity, a następnie zgnieść je do poprzednich. Zobacz [interaktywny |https://help.github.com/en/github/using-git/about-git-rebase] rozdział [rebase |https://help.github.com/en/github/using-git/about-git-rebase] na GitHubie. Po rebasingowaniu zmian, wymuś zmiany do swojego zdalnego widelca, wszystko zostanie automatycznie propagowane do pull request. {{priority: -1}} diff --git a/contributing/pt/code.texy b/contributing/pt/code.texy index 48a8f12bb7..72f3a22d40 100644 --- a/contributing/pt/code.texy +++ b/contributing/pt/code.texy @@ -1,110 +1,118 @@ -Propondo uma mudança no código -****************************** +Contribuindo para o Código +************************** -Nette Framework utiliza Git e [GitHub |https://github.com/nette/nette] para manter a base de códigos. A melhor maneira de contribuir é comprometer suas mudanças em seu próprio garfo e depois fazer um pedido de puxar em GitHub. Este documento resume os principais passos para contribuir com sucesso. +.[perex] +Você está planejando contribuir para a Estrutura Nette e precisa se familiarizar com as regras e procedimentos? Este guia para iniciantes irá guiá-lo através dos passos para contribuir efetivamente com o código, trabalhar com os repositórios e implementar mudanças. -Preparando o ambiente .[#toc-preparing-environment] -=================================================== +Procedimento .[#toc-procedure] +============================== -Comece com o [forking |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette no GitHub |https://github.com/nette]. [Configure |https://help.github.com/en/github/getting-started-with-github/set-up-git] cuidadosamente seu ambiente Git local, configure seu nome de usuário e e-mail, estas credenciais identificarão suas mudanças no histórico do Nette Framework. +Para contribuir com o código, é essencial ter uma conta no [GitHub |https://github.com] e estar familiarizado com os fundamentos do trabalho com o sistema de controle de versões Git. Se você não está familiarizado com Git, você pode verificar o [git - o guia simples |https://rogerdudler.github.io/git-guide/] e considerar o uso de um dos muitos [clientes gráficos |https://git-scm.com/downloads/guis]. -Trabalhando em seu Patch .[#toc-working-on-your-patch] -====================================================== +Preparação do Meio Ambiente e Repositório .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------------------ -Antes de começar a trabalhar em seu patch, crie uma nova filial para suas mudanças. -```shell -git checkout -b new_branch_name -``` +1) No GitHub, crie um [garfo |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] do [repositório de pacotes |www:packages] que você pretende modificar +2) [Clonar |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] este repositório em seu computador +3) Instale as dependências, incluindo o [Nette Tester |tester:], usando o comando `composer install` +4) Verificar se os testes estão funcionando `composer tester` +5) Criar uma [nova filial |#New Branch] com base na última versão lançada -Você pode trabalhar na mudança do seu código. -Se possível, fazer alterações a partir da última versão lançada. +Implementando suas próprias mudanças .[#toc-implementing-your-own-changes] +-------------------------------------------------------------------------- +Agora você pode fazer seus próprios ajustes de código: -Testando suas mudanças .[#toc-testing-your-changes] -=================================================== +1) Implementar as mudanças desejadas e não esquecer os testes +2) Certifique-se de que os testes sejam executados com sucesso usando `composer tester` +3) Verificar se o código atende às [normas de codificação |#coding standards] +4) Salvar (comprometer) as mudanças com uma descrição [neste formato |#Commit Description] + +Você pode criar vários compromissos, um para cada passo lógico. Cada compromisso deve ser significativo por si só. + + +Submetendo mudanças .[#toc-submitting-changes] +---------------------------------------------- + +Uma vez satisfeitos com as mudanças, você pode apresentá-las: + +1) Empurre as mudanças no GitHub para o seu garfo +2) A partir daí, submetê-los ao repositório Nette, criando uma [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Forneça [informações suficientes |#pull request description] na descrição + + +Incorporando Feedback .[#toc-incorporating-feedback] +---------------------------------------------------- + +Seus compromissos agora são visíveis para os outros. É comum receber comentários com sugestões: + +1) Acompanhe as mudanças propostas +2) Incorporá-los como novos compromissos ou [fundi-los com os anteriores |https://help.github.com/en/github/using-git/about-git-rebase] +3) Reenviar os compromissos ao GitHub, e eles aparecerão automaticamente no pedido de puxar + +Nunca criar um novo pedido de puxar para modificar um já existente. + + +Documentação .[#toc-documentation] +---------------------------------- -Você precisa instalar o Nette Tester. A maneira mais fácil é ligar para `composer install` na raiz do repositório. Agora você deve ser capaz de executar testes com `./vendor/bin/tester` no terminal. +Se você mudou de funcionalidade ou adicionou uma nova, não se esqueça de [adicioná-la |documentation] também [à documentação |documentation]. -Alguns testes podem falhar devido à falta do php.ini. Portanto, você deve chamar o corredor com o parâmetro -c e especificar o caminho para o php.ini, por exemplo `./vendor/bin/tester -c ./tests/php.ini`. -Depois que você for capaz de executar os testes, você pode implementar seus próprios testes ou mudar a falha para se adequar ao novo comportamento. Leia mais sobre os testes com o Nette Tester na [página de documentação |tester:]. +Nova filial .[#toc-new-branch] +============================== + +Se possível, faça alterações em relação à última versão lançada, ou seja, a última tag no ramo. Para a tag v3.2.1, criar um ramo usando este comando: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Normas de Codificação .[#toc-coding-standards] ============================================== -Seu código deve seguir o [padrão de codificação |coding standard] utilizado no Nette Framework. É fácil porque há um verificador e um reparador automáticos. Ele pode ser instalado via Composer em seu diretório global escolhido: +Seu código deve atender ao [padrão de codificação |coding standard] utilizado no Nette Framework. Há uma ferramenta automática disponível para verificar e fixar o código. Você pode instalá-lo **globalmente** através do Composer em uma pasta de sua escolha: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Agora você deve ser capaz de executar a ferramenta no terminal. Por exemplo, este comando verifica e corrige o código nas pastas `src` e `tests` no diretório atual: +Agora você deve ser capaz de executar a ferramenta no terminal. O primeiro comando verifica e o segundo conserta o código nas pastas `src` e `tests` no diretório atual: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Cometendo as mudanças .[#toc-committing-the-changes] -==================================================== +Descrição do compromisso .[#toc-commit-description] +=================================================== -Depois de ter mudado o código, você tem que comprometer suas mudanças. Crie mais compromissos, um para cada passo lógico. Cada compromisso deveria ter sido utilizável como está - sem outros compromissos. Portanto, os testes apropriados também devem ser incluídos no mesmo commit. +Em Nette, os assuntos de compromisso têm o seguinte formato: `Presenter: fixed AJAX detection [Closes #69]` -Por favor, verifique novamente se seu código se encaixa nas regras: -- O código não gera nenhum erro -- Seu código não quebra nenhum teste. -- Sua mudança de código é testada. -- Você não está cometendo mudanças inúteis no espaço branco. +- área seguida por um cólon +- objetivo do compromisso no passado; se possível, comece com palavras como: added, fixed, refactored, changed, removed +- se o compromisso quebra a compatibilidade para trás, adicionar "BC break" +- qualquer conexão com o rastreador de problemas, como `(#123)` ou `[Closes #69]` +- após o assunto, pode haver uma linha em branco seguida por uma descrição mais detalhada, incluindo, por exemplo, links para o fórum -A mensagem de compromisso deve seguir o formato `Latte: fixed multi template rendering [Closes # 69]` ou seja -- uma área seguida por um cólon -- o objetivo do compromisso no passado, se possível, começar com "adicionado", "fixo", "refatorado", alterado, removido -- eventual link para o rastreador de emissões -- se a compatibilidade retroativa for cancelada, adicionar "BC break". -- pode haver uma linha gratuita após o assunto e uma descrição mais detalhada incluindo links para o fórum. +Descrição do Pedido de Puxar .[#toc-pull-request-description] +============================================================= -Puxar-Requisitar os Compromissos .[#toc-pull-requesting-the-commits] -==================================================================== +Ao criar um pedido de puxar, a interface GitHub permitirá que você insira um título e uma descrição. Forneça um título conciso e inclua o máximo de informações possíveis na descrição sobre os motivos de sua mudança. -Se você está satisfeito com suas mudanças de código e se compromete, você tem que empurrar seu compromisso com o GitHub. +Além disso, especificar no cabeçalho se é uma nova característica ou uma correção de bug e se pode causar problemas de retrocompatibilidade (BC break). Se houver um problema relacionado, estabeleça um link com ele para que seja fechado após a aprovação do pedido de puxar. -```shell -git push origin new_branch_name ``` - -As mudanças estão presentes publicamente, no entanto, você tem que propor suas mudanças para integração no ramo principal da Nette. Para fazer isso, [faça um pedido de puxar |https://help.github.com/articles/creating-a-pull-request]. -Cada pedido de puxar tem um título e uma descrição. Por favor, forneça algum título descritivo. Muitas vezes é semelhante ao nome da filial, por exemplo "Assegurando sinais contra ataque do CSRF". - -A descrição do pedido deve conter algumas informações mais específicas sobre suas mudanças de código: -``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Favor alterar a tabela de informações para se adequar ao seu pedido de puxar. Comentários a cada item da lista: -- Diz se a solicitação de puxar adiciona ** recurso*** ou é um **bugfix***. -- Referências eventualmente** relacionadas ao ** problema**, que serão fechadas após a fusão da solicitação de puxar. -- Diz se a solicitação pull precisar da **documentação mudar**, se sim, fornecer referências para as solicitações pull apropriadas. Não é necessário fornecer a mudança de documentação imediatamente, entretanto, a solicitação pull não será fundida se for necessária a mudança de documentação. A alteração da documentação deve ser preparada para a documentação em inglês, as mutações em outros idiomas são opcionais. -- Diz que se a solicitação pull criar **a BC break***. Por favor, considere tudo o que muda a interface pública como um intervalo BC. - -A mesa final poderia ser parecida: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Reestruturando suas mudanças .[#toc-reworking-your-changes] -=========================================================== -É realmente comum receber comentários para sua mudança de código. Por favor, tente seguir as mudanças propostas e retrabalhe seus compromissos para fazer isso. Você pode comprometer-se com as mudanças propostas como novos compromissos e depois esmagá-las em relação aos anteriores. Veja o capítulo sobre o GitHub no [Rebase Interactive |https://help.github.com/en/github/using-git/about-git-rebase]. Depois de rebasear suas mudanças, force-push suas mudanças para seu garfo remoto, tudo será automaticamente propagado para o pedido de puxar. {{priority: -1}} diff --git a/contributing/ro/code.texy b/contributing/ro/code.texy index 5fd47ccdc0..c75032d3c9 100644 --- a/contributing/ro/code.texy +++ b/contributing/ro/code.texy @@ -1,110 +1,118 @@ -Propunerea de modificare a codului -********************************** +Contribuția la cod +****************** -Nette Framework folosește Git și [GitHub |https://github.com/nette/nette] pentru menținerea bazei de cod. Cel mai bun mod de a contribui este să vă confirmați modificările în propriul fork și apoi să faceți o cerere de extragere pe GitHub. Acest document rezumă principalii pași pentru a contribui cu succes. +.[perex] +Intenționați să contribuiți la Nette Framework și trebuie să vă familiarizați cu regulile și procedurile? Acest ghid pentru începători vă va ghida prin pașii pentru a contribui eficient la cod, a lucra cu depozitele și a implementa modificări. -Pregătirea mediului .[#toc-preparing-environment] -================================================= +Procedura .[#toc-procedure] +=========================== -Începeți prin a [face forking pentru |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette pe GitHub |https://github.com/nette]. [Pregătiți |https://help.github.com/en/github/getting-started-with-github/set-up-git] cu atenție mediul Git local, configurați-vă numele de utilizator și adresa de e-mail, aceste credențiale vor identifica modificările dvs. în istoricul Nette Framework. +Pentru a contribui la cod, este esențial să aveți un cont pe [GitHub |https://github.com] și să fiți familiarizați cu elementele de bază ale lucrului cu sistemul de control al versiunilor Git. Dacă nu sunteți familiarizat cu Git, puteți consulta [Ghidul git - the simple guide |https://rogerdudler.github.io/git-guide/] și puteți lua în considerare utilizarea unuia dintre numeroșii [clienți grafici |https://git-scm.com/downloads/guis]. -Lucrați la patch-ul dvs. .[#toc-working-on-your-patch] -====================================================== +Pregătirea mediului și a depozitului .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------------- -Înainte de a începe să lucrați la patch-ul dumneavoastră, creați o nouă ramură pentru modificările dumneavoastră. -```shell -git checkout -b new_branch_name -``` +1) Pe GitHub, creați un [fork |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] al [depozitului de pachete |www:packages] pe care intenționați să îl modificați +2) [Clonați |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] acest depozit pe computerul dvs. +3) Instalați dependențele, inclusiv [Nette Tester |tester:], utilizând comanda `composer install` +4) Verificați dacă testele funcționează, rulând `composer tester` +5) Creați o [nouă ramură |#New Branch] bazată pe cea mai recentă versiune lansată -Puteți lucra la modificarea codului dumneavoastră. -Dacă este posibil, efectuați modificările din ultima versiune lansată. +Implementarea propriilor modificări .[#toc-implementing-your-own-changes] +------------------------------------------------------------------------- +Acum puteți face propriile modificări de cod: -Testarea modificărilor dvs. .[#toc-testing-your-changes] -======================================================== +1) Implementați modificările dorite și nu uitați de teste +2) Asigurați-vă că testele se execută cu succes folosind `composer tester` +3) Verificați dacă codul respectă [standardele de codare |#coding standards] +4) Salvați (confirmați) modificările cu o descriere în [acest format |#Commit Description] -Trebuie să instalați Nette Tester. Cel mai simplu este să apelați `composer install` în rădăcina depozitului. Acum ar trebui să puteți rula teste cu `./vendor/bin/tester` în terminal. +Puteți crea mai multe comenzi, câte una pentru fiecare etapă logică. Fiecare commit ar trebui să fie semnificativ în sine. -Este posibil ca unele teste să eșueze din cauza lipsei php.ini. Prin urmare, ar trebui să apelați runner-ul cu parametrul -c și să specificați calea către php.ini, de exemplu `./vendor/bin/tester -c ./tests/php.ini`. -După ce puteți rula testele, puteți să le implementați pe ale dumneavoastră sau să modificați eșecul pentru a corespunde noului comportament. Citiți mai multe despre testarea cu Nette Tester în [pagina de documentație |tester:]. +Trimiterea modificărilor .[#toc-submitting-changes] +--------------------------------------------------- +După ce sunteți mulțumit de modificări, le puteți trimite: -Standarde de codare .[#toc-coding-standards] -============================================ +1) Împingeți modificările pe GitHub în furculița dvs. +2) De acolo, trimiteți-le la depozitul Nette prin crearea unei [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Furnizați [informații suficiente |#pull request description] în descriere -Codul dumneavoastră trebuie să respecte [standardele de codare |coding standard] utilizate în Nette Framework. Este ușor, deoarece există un verificator și un fixator automat. Acesta poate fi instalat prin Composer în directorul global ales de dumneavoastră: -```shell -composer create-project nette/coding-standard /path/to/nette-coding-standard -``` +Încorporarea feedback-ului .[#toc-incorporating-feedback] +--------------------------------------------------------- -Acum ar trebui să puteți rula instrumentul în terminal. De exemplu, această comandă verifică și corectează codul din dosarele `src` și `tests` din directorul curent: +Modificările dvs. sunt acum vizibile pentru ceilalți. Este obișnuit să primiți comentarii cu sugestii: -```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix -``` +1) Țineți evidența modificărilor propuse +2) Încorporați-le ca noi comenzi sau [fuzionați-le cu cele anterioare |https://help.github.com/en/github/using-git/about-git-rebase] +3) Trimiteți din nou comentariile pe GitHub, iar acestea vor apărea automat în cererea de extragere (pull request) +Nu creați niciodată un nou pull request pentru a modifica unul existent. -Trimiterea modificărilor .[#toc-committing-the-changes] -======================================================= -După ce ați modificat codul, trebuie să vă confirmați modificările. Creați mai multe comisioane, câte una pentru fiecare pas logic. Fiecare commit trebuie să fi fost utilizabil ca atare - fără alte commits. Așadar, testele corespunzătoare ar trebui să fie, de asemenea, incluse în același commit. +Documentație .[#toc-documentation] +---------------------------------- -Vă rugăm să verificați de două ori dacă codul dvs. se încadrează în reguli: -- Codul nu generează nicio eroare -- Codul dumneavoastră nu întrerupe niciun test. -- Modificarea codului dvs. este testată. -- Nu comiteți modificări inutile de spațiu alb. +Dacă ați modificat o funcționalitate sau ați adăugat una nouă, nu uitați să [o adăugați și în documentație |documentation]. -Mesajul de confirmare trebuie să respecte formatul `Latte: fixed multi template rendering [Closes # 69]` adică: -- o zonă urmată de două puncte -- scopul confirmării în trecut, dacă este posibil, începeți cu "added.", "fixed.", "refactored.", changed, removed -- o eventuală legătură către issue tracker -- dacă trimiterea anulează compatibilitatea retroactivă, adăugați "BC break". -- poate exista o linie liberă după subiect și o descriere mai detaliată, inclusiv linkuri către forum. +Ramură nouă .[#toc-new-branch] +============================== -Solicitarea de comenzi de tip Pull-Request .[#toc-pull-requesting-the-commits] -============================================================================== - -Dacă sunteți mulțumiți de modificările aduse codului și de angajamentele dvs., trebuie să trimiteți aceste angajamente pe GitHub. +Dacă este posibil, efectuați modificările în raport cu ultima versiune publicată, adică ultima etichetă din ramură. Pentru eticheta v3.2.1, creați o ramură folosind această comandă: ```shell -git push origin new_branch_name +git checkout -b new_branch_name v3.2.1 ``` -Modificările sunt prezente în mod public, însă trebuie să vă propuneți modificările pentru a fi integrate în ramura principală a Nette. Pentru a face acest lucru, [faceți o cerere de extragere |https://help.github.com/articles/creating-a-pull-request]. -Fiecare pull request are un titlu și o descriere. Vă rugăm să furnizați un titlu descriptiv. Acesta este adesea similar cu numele ramurii, de exemplu "Securing signals against CSRF attack". -Descrierea pull request ar trebui să fi conținut câteva informații mai specifice despre modificările aduse codului dvs: -``` -- bug fix? yes/no -- new feature? yes/no -- BC break? yes/no -- doc PR: nette/docs#??? -``` +Standarde de codificare .[#toc-coding-standards] +================================================ -Vă rugăm să modificați tabelul de informații pentru a se potrivi cererii dvs. de extragere. Comentarii la fiecare element din listă: -- Spune dacă cererea de extragere adaugă **funcționalitate** sau este o **reparare de erori**. -- Face trimitere la o eventuală **problemă conexă**, care va fi închisă după fuzionarea cererii de extragere. -- Spune dacă pull request-ul are nevoie de **modificări ale documentației**, dacă da, oferă referințe la pull requests corespunzătoare. Nu trebuie să furnizezi imediat modificarea documentației, însă cererea nu va fi fuzionată dacă este necesară modificarea documentației. Modificarea documentației trebuie să fie pregătită pentru documentația în limba engleză, alte mutații de limbă sunt opționale. -- Spune dacă pull request-ul creează **o întrerupere a BC**. Vă rugăm să considerați tot ceea ce modifică interfața publică ca fiind o întrerupere BC. +Codul dumneavoastră trebuie să respecte [standardele de codare |coding standard] utilizate în cadrul Nette Framework. Există un instrument automat disponibil pentru verificarea și corectarea codului. Îl puteți instala **global** prin Composer într-un dosar la alegere: -Tabelul final ar putea arăta astfel: +```shell +composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no + +Acum ar trebui să puteți rula instrumentul în terminal. Prima comandă verifică, iar cea de-a doua corectează codul din dosarele `src` și `tests` din directorul curent: + +```shell +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Refacerea modificărilor tale .[#toc-reworking-your-changes] -=========================================================== +Angajare Descriere .[#toc-commit-description] +============================================= + +În Nette, subiectele de commit au următorul format: `Presenter: fixed AJAX detection [Closes #69]` + +- area urmată de două puncte +- scopul angajamentului la timpul trecut; dacă este posibil, începeți cu cuvinte de genul: added, fixed, refactored, changed, removed +- în cazul în care confirmarea încalcă compatibilitatea retroactivă, adăugați "BC break" +- orice legătură cu sistemul de urmărire a problemelor, cum ar fi `(#123)` sau `[Closes #69]` +- după subiect, poate exista o linie goală, urmată de o descriere mai detaliată, incluzând, de exemplu, linkuri către forum + + +Descrierea cererii de tip pull request .[#toc-pull-request-description] +======================================================================= + +Atunci când creați un pull request, interfața GitHub vă va permite să introduceți un titlu și o descriere. Furnizați un titlu concis și includeți în descriere cât mai multe informații despre motivele modificării dumneavoastră. + +De asemenea, precizați în antet dacă este o caracteristică nouă sau o remediere a unei erori și dacă poate cauza probleme de compatibilitate retroactivă (BC break). Dacă există o problemă conexă, creați un link către aceasta, astfel încât aceasta să fie închisă în momentul aprobării cererii de modificare. + +``` +- bug fix / new feature? +- BC break? yes/no +- doc PR: nette/docs#? +``` -Este foarte frecvent să primiți comentarii la modificarea codului dumneavoastră. Vă rugăm să încercați să respectați modificările propuse și să vă reelaborați comenzile pentru a face acest lucru. Puteți să confirmați modificările propuse ca noi confirmări și apoi să le striviți cu cele anterioare. Consultați capitolul [Rebase interactiv |https://help.github.com/en/github/using-git/about-git-rebase] pe GitHub. După ce ați făcut rebase, împingeți forțat modificările în furculița dvs. la distanță, totul se va propaga automat în cererea de extragere. {{priority: -1}} diff --git a/contributing/ru/code.texy b/contributing/ru/code.texy index 801c7418a0..9e2239ab22 100644 --- a/contributing/ru/code.texy +++ b/contributing/ru/code.texy @@ -1,110 +1,118 @@ -Предложение об изменении кодекса -******************************** +Вклад в код +*********** -Nette Framework использует Git и [GitHub |https://github.com/nette/nette] для поддержки кодовой базы. Лучший способ внести свой вклад - зафиксировать свои изменения в собственном форке, а затем сделать pull request на GitHub. В этом документе кратко описаны основные шаги для успешного внесения изменений. +.[perex] +Вы планируете внести свой вклад в Nette Framework и вам необходимо ознакомиться с правилами и процедурами? Это руководство для начинающих расскажет вам о том, как эффективно вносить вклад в код, работать с репозиториями и внедрять изменения. -Подготовка среды .[#toc-preparing-environment] -============================================== +Процедура .[#toc-procedure] +=========================== -Начните с [форка |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette на GitHub |https://github.com/nette]. Тщательно [настройте |https://help.github.com/en/github/getting-started-with-github/set-up-git] локальную среду Git, задайте имя пользователя и email, эти учетные данные будут идентифицировать ваши изменения в истории Nette Framework. +Чтобы внести свой вклад в код, необходимо иметь учетную запись на [GitHub |https://github.com] и быть знакомым с основами работы с системой контроля версий Git. Если вы не знакомы с Git, вы можете ознакомиться с [git - простым руководством |https://rogerdudler.github.io/git-guide/] и рассмотреть возможность использования одного из многочисленных [графических клиентов |https://git-scm.com/downloads/guis]. -Работа над вашим патчем .[#toc-working-on-your-patch] -===================================================== +Подготовка среды и репозитория .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------- -Прежде чем начать работу над патчем, создайте новую ветвь для своих изменений. -```shell -git checkout -b new_branch_name -``` +1) На GitHub создайте [форк |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [репозитория пакета |www:packages], который вы собираетесь изменить +2) [Клонируйте |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] этот репозиторий на свой компьютер +3) Установите зависимости, включая [Nette Tester |tester:], с помощью команды `composer install`. +4) Убедитесь, что тесты работают, выполнив команду `composer tester` +5) Создайте [новую ветку |#New Branch] на основе последней выпущенной версии + + +Внедрение собственных изменений .[#toc-implementing-your-own-changes] +--------------------------------------------------------------------- + +Теперь вы можете внести собственные изменения в код: + +1) Внесите желаемые изменения и не забудьте о тестах +2) Убедитесь, что тесты успешно выполняются `composer tester` +3) Проверьте, соответствует ли код [стандартам кодирования |#coding standards] +4) Сохраните (зафиксируйте) изменения с описанием в [таком формате |#Commit Description] + +Вы можете создать несколько коммитов, по одному для каждого логического шага. Каждый коммит должен быть значимым сам по себе. -Вы можете работать над изменением своего кода. -Если возможно, вносите изменения из последней выпущенной версии. +Представление изменений .[#toc-submitting-changes] +-------------------------------------------------- +После того как вы будете удовлетворены изменениями, вы можете отправить их: -Тестирование ваших изменений .[#toc-testing-your-changes] -========================================================= +1) Внесите изменения на GitHub в свой форк. +2) Оттуда отправьте их в репозиторий Nette, создав [pull request|https://help.github.com/articles/creating-a-pull-request] внесение изменений (PR). +3) Предоставьте [достаточную информацию |#pull request description] в описании -Вам необходимо установить Nette Tester. Самый простой способ - вызвать `composer install` в корне репозитория. Теперь вы должны иметь возможность запускать тесты с `./vendor/bin/tester` в терминале. -Некоторые тесты могут не работать из-за отсутствия php.ini. Поэтому следует вызвать бегунок с параметром -c и указать путь к php.ini, например, `./vendor/bin/tester -c ./tests/php.ini`. +Включение обратной связи .[#toc-incorporating-feedback] +------------------------------------------------------- -После того, как вы сможете запустить тесты, вы можете реализовать свои собственные или изменить отказ, чтобы соответствовать новому поведению. Подробнее о тестировании с помощью Nette Tester читайте на [странице документации |tester:]. +Теперь ваши коммиты видны другим. Часто можно получить комментарии с предложениями: + +1) Следить за предлагаемыми изменениями +2) Включить их в новые коммиты или [объединить с предыдущими |https://help.github.com/en/github/using-git/about-git-rebase] +3) Повторно отправить исправления на GitHub, и они автоматически появятся в запросе на исправление. + +Никогда не создавайте новый pull request для изменения существующего. + + +Документация .[#toc-documentation] +---------------------------------- + +Если вы изменили функциональность или добавили новую, не забудьте также [добавить ее в документацию |documentation]. + + +Новый филиал .[#toc-new-branch] +=============================== + +Если возможно, вносите изменения в последнюю выпущенную версию, т.е. в последний тег в ветке. Для тега v3.2.1 создайте ветку с помощью этой команды: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Стандарты кодирования .[#toc-coding-standards] ============================================== -Ваш код должен соответствовать [стандартам кодирования |coding standard], используемым в Nette Framework. Это легко, потому что есть автоматический чекер и фиксер. Он может быть установлен через Composer в выбранный вами глобальный каталог: +Ваш код должен соответствовать [стандартам кодирования |coding standard], используемым в Nette Framework. Существует автоматический инструмент для проверки и исправления кода. Вы можете установить его **глобально** через Composer в выбранную вами папку: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Теперь вы должны быть в состоянии запустить инструмент в терминале. Например, эта команда проверяет и исправляет код в папках `src` и `tests` в текущем каталоге: +Теперь вы должны быть в состоянии запустить инструмент в терминале. Первая команда проверяет, а вторая исправляет код в папках `src` и `tests` в текущем каталоге: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Зафиксировать изменения .[#toc-committing-the-changes] -====================================================== - -После того как вы изменили код, необходимо зафиксировать изменения. Создайте несколько коммитов, по одному на каждый логический шаг. Каждый коммит должен быть пригоден для использования как есть - без других коммитов. Поэтому соответствующие тесты должны быть включены в тот же коммит. +Описание обязательств .[#toc-commit-description] +================================================ -Пожалуйста, дважды проверьте, соответствует ли ваш код правилам: -- Код не генерирует никаких ошибок -- Ваш код не нарушает никаких тестов. -- Ваше изменение кода протестировано. -- Вы не совершаете бесполезных изменений в белых пятнах. +В Nette темы коммитов имеют следующий формат: `Presenter: fixed AJAX detection [Closes #69]` -Сообщение о фиксации должно соответствовать формату `Latte: fixed multi template rendering [Closes # 69]` т.е: - область, за которой следует двоеточие -- цель фиксации в прошлом, если возможно, начинайте с "добавлено.", "исправлено.", "рефакторинговано.", изменено, удалено -- возможная ссылка на трекер проблем -- если коммит отменяет обратную совместимость, добавьте "BC break". -- после темы может быть одна свободная строка и более подробное описание, включая ссылки на форум. +- цель фиксации в прошедшем времени; если возможно, начинайте с таких слов, как: added, fixed, refactored, changed, removed +- если коммит нарушает обратную совместимость, добавьте "BC break" +- любая связь с трекером проблем, например, `(#123)` или `[Closes #69]` +- после темы может быть одна пустая строка, за которой следует более подробное описание, включая, например, ссылки на форум -Pull-Requesting the Commits .[#toc-pull-requesting-the-commits] -=============================================================== +Описание Pull Request .[#toc-pull-request-description] +====================================================== -Если вы удовлетворены своими изменениями и коммитами в коде, вы должны опубликовать свои коммиты на GitHub. +При создании pull request интерфейс GitHub позволит вам ввести название и описание. Укажите лаконичное название и включите в описание как можно больше информации о причинах вашего изменения. -```shell -git push origin new_branch_name -``` +Также укажите в заголовке, является ли это новой функцией или исправлением ошибки, и может ли это привести к проблемам обратной совместимости (BC break). Если существует связанная с ним проблема, укажите ссылку на нее, чтобы она была закрыта после одобрения запроса. -Изменения представлены публично, однако вы должны предложить свои изменения для интеграции в мастер-ветку Nette. Для этого сделайте [запрос на |https://help.github.com/articles/creating-a-pull-request] исправление. -Каждый запрос имеет заголовок и описание. Пожалуйста, предоставьте какое-нибудь описывающее название. Часто оно похоже на название ветки, например, "Защита сигналов от CSRF-атак". - -Описание запроса на исправление должно содержать более конкретную информацию о ваших изменениях кода: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Пожалуйста, измените информационную таблицу в соответствии с вашим запросом. Комментарии к каждому пункту списка: -- Указывается, добавляет ли запрос на исправление **фичу** или это **исправление**. -- Ссылается на **связанный вопрос**, который будет закрыт после объединения заявки. -- Говорится, нужны ли в заявке **изменения документации**, если да, то укажите ссылки на соответствующие заявки. Вы не обязаны предоставлять изменения документации немедленно, однако запрос на исправление не будет объединен, если изменения документации необходимы. Изменение документации должно быть подготовлено для английской документации, мутации других языков необязательны. -- Говорится, если запрос на изменение создаёт **разрыв BC**. Пожалуйста, считайте все, что изменяет публичный интерфейс, нарушением BC. - -Итоговая таблица может выглядеть следующим образом: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Переработка ваших изменений .[#toc-reworking-your-changes] -========================================================== -Очень часто приходится получать комментарии к вашим изменениям в коде. Пожалуйста, старайтесь следовать предложенным изменениям и перерабатывайте свои коммиты для этого. Вы можете зафиксировать предложенные изменения как новые коммиты, а затем раздавить их на предыдущие. См. главу [Интерактивный rebase |https://help.github.com/en/github/using-git/about-git-rebase] на GitHub. После перебазирования изменений, принудительно отправьте изменения в ваш удаленный форк, все будет автоматически распространено в pull request. {{priority: -1}} diff --git a/contributing/sl/code.texy b/contributing/sl/code.texy index ba69b54e6f..968d13cd56 100644 --- a/contributing/sl/code.texy +++ b/contributing/sl/code.texy @@ -1,110 +1,118 @@ -Predlaganje spremembe kodeksa -***************************** +Prispevek h kodi +**************** -Nette Framework za vzdrževanje baze kode uporablja Git in [GitHub |https://github.com/nette/nette]. Najboljši način za prispevanje je, da svoje spremembe prenesete v svojo vilico in nato v GitHub oddate zahtevek za povleko. Ta dokument povzema glavne korake za uspešno prispevanje. +.[perex] +Ali nameravate prispevati k ogrodju Nette in se morate seznaniti s pravili in postopki? Ta priročnik za začetnike vas bo popeljal skozi korake za učinkovito prispevanje h kodi, delo z repozitoriji in izvajanje sprememb. -Priprava okolja .[#toc-preparing-environment] -============================================= +Postopek .[#toc-procedure] +========================== -Začnite z [viličenjem |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette na GitHubu |https://github.com/nette]. Skrbno [nastavite |https://help.github.com/en/github/getting-started-with-github/set-up-git] lokalno okolje Git, konfigurirajte svoje uporabniško ime in e-pošto, saj bodo te poverilnice identificirale vaše spremembe v zgodovini ogrodja Nette. +Če želite prispevati h kodi, morate imeti račun na [GitHubu |https://github.com] in poznati osnove dela s sistemom za nadzor različic Git. Če sistema Git ne poznate, si lahko ogledate priročnik [git - the simple guide |https://rogerdudler.github.io/git-guide/] in razmislite o uporabi enega od številnih [grafičnih odjemalcev |https://git-scm.com/downloads/guis]. -Delo na vašem popravku .[#toc-working-on-your-patch] -==================================================== +Priprava okolja in skladišča .[#toc-preparing-the-environment-and-repository] +----------------------------------------------------------------------------- -Preden začnete delati na popravku, ustvarite novo vejo za svoje spremembe. -```shell -git checkout -b new_branch_name -``` +1) Na spletnem mestu GitHub ustvarite [vilico |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [skladišča paketov |www:packages], ki ga nameravate spremeniti +2) [Klonirate |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] to shrambo v svoj računalnik +3) Z ukazom `composer install` namestite odvisnosti, vključno s [programom Nette Tester |tester:]. +4) Preverite, ali testi delujejo, tako da zaženete `composer tester` +5) Ustvarite [novo vejo, ki |#New Branch] temelji na zadnji izdani različici + + +Izvajanje lastnih sprememb .[#toc-implementing-your-own-changes] +---------------------------------------------------------------- + +Zdaj lahko izvedete lastne prilagoditve kode: + +1) Izvedite želene spremembe in ne pozabite na teste +2) Poskrbite, da se testi uspešno izvedejo z uporabo `composer tester` +3) Preverite, ali koda ustreza [standardom kodiranja |#coding standards] +4) Shranite (commit) spremembe z opisom v [tej |#Commit Description]obliki + +Ustvarite lahko več zavez, po eno za vsak logični korak. Vsaka oddaja mora biti smiselna sama po sebi. + + +Oddaja sprememb .[#toc-submitting-changes] +------------------------------------------ + +Ko ste s spremembami zadovoljni, jih lahko predložite: + +1) Spremembe prenesite na GitHub v svojo vilico +2) Od tam jih predložite v skladišče Nette tako, da ustvarite [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) V opisu navedite [dovolj informacij |#pull request description] + + +Vključevanje povratnih informacij .[#toc-incorporating-feedback] +---------------------------------------------------------------- + +Vaše oddaje so zdaj vidne drugim. Pogosto prejmete komentarje s predlogi: -Tako lahko delate na spremembi kode. +1) Spremljajte predlagane spremembe +2) Vključite jih kot nove spremembe ali [jih združite s prejšnjimi |https://help.github.com/en/github/using-git/about-git-rebase] +3) Ponovno pošljite spremembe v GitHub in samodejno se bodo pojavile v zahtevi za povlečenje. -Če je mogoče, spremembe izvedite iz zadnje izdane različice. +Nikoli ne ustvarjajte nove zahteve za prenos, da bi spremenili obstoječo zahtevo. -Preizkušanje sprememb .[#toc-testing-your-changes] -================================================== +Dokumentacija .[#toc-documentation] +----------------------------------- -Namestiti morate program Nette Tester. Najlažje je, če v korenskem naslovu skladišča pokličete `composer install`. Zdaj bi morali biti sposobni izvajati teste s `./vendor/bin/tester` v terminalu. +Če ste spremenili funkcionalnost ali dodali novo, ne pozabite [tega dodati tudi v dokumentacijo |documentation]. -Nekateri testi bodo morda neuspešni zaradi manjkajočega php.ini. Zato morate prožilec poklicati s parametrom -c in navesti pot do php.ini, na primer `./vendor/bin/tester -c ./tests/php.ini`. -Ko boste lahko zagnali teste, lahko izvedete svoje ali spremenite neuspešne, da bodo ustrezali novemu obnašanju. Več o testiranju s programom Nette Tester si preberite na [strani z dokumentacijo |tester:]. +Nova veja .[#toc-new-branch] +============================ + +Če je mogoče, spremembe izvedite glede na zadnjo izdano različico, tj. zadnjo oznako v veji. Za oznako v3.2.1 ustvarite vejo s tem ukazom: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Standardi kodiranja .[#toc-coding-standards] ============================================ -Vaša koda mora upoštevati [standard kodiranja, ki |coding standard] se uporablja v okviru Nette. To je enostavno, saj je na voljo samodejni pregledovalnik in popravljalnik. Namestite ga lahko prek programa Composer v izbrani globalni imenik: +Vaša koda mora ustrezati [standardom kodiranja, |coding standard] ki se uporabljajo v okviru Nette. Za preverjanje in popravljanje kode je na voljo samodejno orodje. Namestite ga lahko **globalno** prek programa Composer v izbrano mapo: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Zdaj morate biti sposobni zagnati orodje v terminalu. Ta ukaz na primer preveri in popravi kodo v mapah `src` in `tests` v trenutnem imeniku: +Orodje lahko zaženete v terminalu. Prvi ukaz preveri, drugi pa popravi kodo v mapah `src` in `tests` v trenutnem imeniku: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Oddajanje sprememb .[#toc-committing-the-changes] -================================================= +Obveznost Opis .[#toc-commit-description] +========================================= -Ko spremenite kodo, morate spremembe potrditi. Ustvarite več zavez, po eno za vsak logični korak. Vsaka sprememba mora biti uporabna takšna, kot je - brez drugih sprememb. Zato je treba v isti commit vključiti tudi ustrezne teste. +V sistemu Nette imajo predmeti sprememb naslednjo obliko: `Presenter: fixed AJAX detection [Closes #69]` -Dvakrat preverite, ali vaša koda ustreza pravilom: -- Koda ne ustvarja napak. -- Vaša koda ne krši nobenih testov. -- Vaša sprememba kode je testirana. -- Ne izvajate nekoristnih sprememb v belem polju. +- sledi dvopičje +- namen zaveze v preteklem času; če je mogoče, začnite z besedami, kot so: added, fixed, refactored, changed, removed +- če oddaja krši združljivost za nazaj, dodajte "BC break" +- kakršno koli povezavo s programom za sledenje težavam, na primer `(#123)` ali `[Closes #69]` +- za temo je lahko ena prazna vrstica, ki ji sledi podrobnejši opis, na primer vključno s povezavami do foruma -Sporočilo o oddaji mora biti v obliki `Latte: fixed multi template rendering [Closes # 69]` tj: -- področje, ki mu sledi dvopičje -- namen oddaje v preteklosti, če je mogoče, začnite z "dodano.", "popravljeno.", "refaktorizirano.", spremenjeno, odstranjeno -- morebitna povezava do sledilnika težav -- če sprememba prekliče združljivost za nazaj, dodajte "BC break". -- za temo je lahko ena prosta vrstica in podrobnejši opis, vključno s povezavami na forum. +Opis zahtevka za izvleček .[#toc-pull-request-description] +========================================================== -Zahteva za povišanje (Pull-Requesting) za dopolnitve .[#toc-pull-requesting-the-commits] -======================================================================================== - -Če ste zadovoljni s spremembami kode in zavezami, morate svoje zaveze poslati v GitHub. - -```shell -git push origin new_branch_name -``` +Pri ustvarjanju zahteve za prenos vam vmesnik GitHub omogoča vnos naslova in opisa. Navedite jedrnat naslov in v opis vključite čim več informacij o razlogih za vašo spremembo. -Spremembe so javno dostopne, vendar morate svoje spremembe predlagati za vključitev v glavno vejo Nette. To storite tako, da podate [zahtevo za poteg |https://help.github.com/articles/creating-a-pull-request]. -Vsak zahtevek ima naslov in opis. Navedite nekaj opisnih naslovov. Pogosto je podoben imenu veje, na primer "Zavarovanje signalov pred napadom CSRF". +V naslovu navedite tudi, ali gre za novo funkcijo ali popravek napake in ali lahko povzroči težave s povratno združljivostjo (BC break). Če obstaja povezana težava, se nanjo povežite, tako da bo po odobritvi zahteve za spremembo zaprta. -Opis zahteve za poteg bi moral vsebovati nekaj bolj specifičnih informacij o vaših spremembah kode: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Prosimo, spremenite tabelo z informacijami, da bo ustrezala vaši zahtevi za poteg. Pripombe k vsaki postavki seznama: -- Piše, ali zahtevek dodaja **funkcijo** ali gre za **opravek napake**. -- Sklicuje se na morebitno **povezano vprašanje**, ki bo zaprto po združitvi zahteve. -- Pove, ali je treba v zahtevi za prenos spremeniti **dokumentacijo**, če je tako, navedite sklice na ustrezne zahteve za prenos. Spremembe dokumentacije vam ni treba zagotoviti takoj, vendar pa zahteva za poteg ne bo združena, če je sprememba dokumentacije potrebna. Sprememba dokumentacije mora biti pripravljena za angleško dokumentacijo, druge jezikovne mutacije niso obvezne. -- Pove, če zahtevek za prenos ustvari **prekinitev BC**. Vse, kar spreminja javni vmesnik, obravnavajte kot prelom BC. - -Končna tabela bi lahko bila videti takole: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Preoblikovanje sprememb .[#toc-reworking-your-changes] -====================================================== -Pogosto se zgodi, da prejmete komentarje k spremembi kode. Poskusite slediti predlaganim spremembam in v ta namen predelajte svoje spremembe. Predlagane spremembe lahko oddate kot nove oddaje in jih nato zmečkate s prejšnjimi. Oglejte si poglavje o [interaktivni ponovni vzpostavitvi |https://help.github.com/en/github/using-git/about-git-rebase] na spletnem mestu GitHub. Po ponovnem uokvirjanju sprememb za silo potisnite spremembe v oddaljeno vilico, vse se bo samodejno razširilo v zahtevek za povleko (pull request). {{priority: -1}} diff --git a/contributing/tr/code.texy b/contributing/tr/code.texy index d35b0d7faa..ad80a6f0e0 100644 --- a/contributing/tr/code.texy +++ b/contributing/tr/code.texy @@ -1,110 +1,118 @@ -Kanun Değişikliği Önerisi -************************* +Koda Katkıda Bulunma +******************** -Nette Framework, kod tabanını korumak için Git ve [GitHub |https://github.com/nette/nette] 'ı kullanır. Katkıda bulunmanın en iyi yolu, değişikliklerinizi kendi çatalınıza işlemek ve ardından GitHub'da bir çekme isteği yapmaktır. Bu belge, başarılı bir şekilde katkıda bulunmak için önemli adımları özetlemektedir. +.[perex] +Nette Framework'e katkıda bulunmayı planlıyor ve kural ve prosedürlere aşina olmanız mı gerekiyor? Bu başlangıç kılavuzu, koda etkili bir şekilde katkıda bulunma, depolarla çalışma ve değişiklikleri uygulama adımlarında size yol gösterecektir. -Ortam Hazırlama .[#toc-preparing-environment] -============================================= +Prosedür .[#toc-procedure] +========================== -[GitHub'da Nette'yi |https://github.com/nette] [çatallamakla |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] başlayın. Yerel Git ortamınızı dikkatlice [kurun |https://help.github.com/en/github/getting-started-with-github/set-up-git], kullanıcı adınızı ve e-postanızı yapılandırın, bu kimlik bilgileri Nette Framework geçmişindeki değişikliklerinizi tanımlayacaktır. +Koda katkıda bulunmak için [GitHub |https://github.com] 'da bir hesabınızın olması ve Git sürüm kontrol sistemiyle çalışmanın temellerine aşina olmanız gerekir. Git'e aşina değilseniz, [git - the simple guide |https://rogerdudler.github.io/git-guide/] 'a göz atabilir ve birçok [grafik istemciden |https://git-scm.com/downloads/guis] birini kullanmayı düşünebilirsiniz. -Yamanız Üzerinde Çalışmak .[#toc-working-on-your-patch] -======================================================= +Ortamın ve Deponun Hazırlanması .[#toc-preparing-the-environment-and-repository] +-------------------------------------------------------------------------------- -Yamanız üzerinde çalışmaya başlamadan önce, değişiklikleriniz için yeni bir dal oluşturun. -```shell -git checkout -b new_branch_name -``` +1) GitHub'da, değiştirmeyi düşündüğünüz [paket deposunun |www:packages] bir [çatalını |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] oluşturun +2) Bu depoyu bilgisayarınıza [klonlayın |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] +3) `composer install` komutunu kullanarak [Nette Tester |tester:] dahil olmak üzere bağımlılıkları yükleyin +4) Çalıştırarak testlerin çalıştığını doğrulayın `composer tester` +5) Yayınlanan en son sürüme göre [yeni |#New Branch] bir [dal |#New Branch] oluşturun + + +Kendi Değişikliklerinizin Uygulanması .[#toc-implementing-your-own-changes] +--------------------------------------------------------------------------- + +Artık kendi kod ayarlamalarınızı yapabilirsiniz: + +1) İstenen değişiklikleri uygulayın ve testleri unutmayın +2) Testlerin başarıyla çalıştığından emin olmak için `composer tester` +3) Kodun [kodlama standartlarını |#coding standards]karşılayıp karşılamadığını kontrol edin +4) Değişiklikleri [aşağıdaki biçimde |#Commit Description]bir açıklama ile kaydedin (işleyin) + +Her mantıksal adım için bir tane olmak üzere birden fazla taahhüt oluşturabilirsiniz. Her commit kendi başına anlamlı olmalıdır. + + +Değişikliklerin Gönderilmesi .[#toc-submitting-changes] +------------------------------------------------------- + +Değişikliklerden memnun kaldığınızda, bunları gönderebilirsiniz: + +1) Değişiklikleri GitHub'a çatalınıza gönderin +2) Oradan, bir [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) oluşturarak bunları Nette deposuna gönderin +3) Açıklamada [yeterli bilgi |#pull request description] sağlayın + + +Geri Bildirimin Dahil Edilmesi .[#toc-incorporating-feedback] +------------------------------------------------------------- + +Taahhütleriniz artık başkaları tarafından görülebilir. Öneriler içeren yorumlar almak yaygındır: + +1) Önerilen değişiklikleri takip edin +2) Bunları yeni taahhütler olarak dahil edin veya [öncekilerle birleştirin |https://help.github.com/en/github/using-git/about-git-rebase] +3) İşlemleri GitHub'a yeniden gönderin; otomatik olarak çekme isteğinde görüneceklerdir -Kod değişikliğiniz üzerinde çalışabilirsiniz. +Var olanı değiştirmek için asla yeni bir çekme isteği oluşturmayın. -Mümkünse, son yayınlanan sürümden değişiklikler yapın. +Dokümantasyon .[#toc-documentation] +----------------------------------- -Değişikliklerinizi Test Etme .[#toc-testing-your-changes] -========================================================= +İşlevselliği değiştirdiyseniz veya yeni bir işlev eklediyseniz, [bunu belgelere eklem |documentation] eyi de unutmayın. -Nette Tester'ı yüklemeniz gerekir. En kolay yol, depo kökünde `composer install` adresini çağırmaktır. Şimdi terminalde `./vendor/bin/tester` ile testleri çalıştırabilmelisiniz. -Bazı testler eksik php.ini nedeniyle başarısız olabilir. Bu nedenle çalıştırıcıyı -c parametresi ile çağırmalı ve php.ini yolunu belirtmelisiniz, örneğin `./vendor/bin/tester -c ./tests/php.ini`. +Yeni Şube .[#toc-new-branch] +============================ -Testleri çalıştırabildikten sonra, kendi testlerinizi uygulayabilir veya yeni davranışa uyması için başarısız olanı değiştirebilirsiniz. Nette Tester ile test etme hakkında daha fazla bilgiyi [dokümantasyon sayfasında |tester:] bulabilirsiniz. +Mümkünse, değişiklikleri en son yayınlanan sürüme, yani daldaki son etikete göre yapın. v3.2.1 etiketi için bu komutu kullanarak bir dal oluşturun: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Kodlama Standartları .[#toc-coding-standards] ============================================= -Kodunuz Nette Framework'te kullanılan [kodlama standardına |coding standard] uygun olmalıdır. Otomatik denetleyici ve düzeltici olduğu için kolaydır. Composer aracılığıyla seçtiğiniz global dizine kurulabilir: +Kodunuz Nette Framework'te kullanılan [kodlama standardını |coding standard] karşılamalıdır. Kodu kontrol etmek ve düzeltmek için otomatik bir araç mevcuttur. Composer aracılığıyla **global olarak** istediğiniz bir klasöre yükleyebilirsiniz: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Şimdi aracı terminalde çalıştırabilmelisiniz. Örneğin, bu komut geçerli dizindeki `src` ve `tests` klasörlerindeki kodu kontrol eder ve düzeltir: +Şimdi aracı terminalde çalıştırabilmelisiniz. İlk komut kontrol eder ve ikincisi geçerli dizindeki `src` ve `tests` klasörlerindeki kodu düzeltir: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Değişikliklerin İşlenmesi .[#toc-committing-the-changes] -======================================================== - -Kodu değiştirdikten sonra değişikliklerinizi commit etmeniz gerekir. Her mantıksal adım için bir tane olmak üzere daha fazla commit oluşturun. Her bir commit, diğer commitler olmadan olduğu gibi kullanılabilir olmalıdır. Dolayısıyla, uygun testler de aynı commit'e dahil edilmelidir. - -Lütfen kodunuzun kurallara uygunluğunu iki kez kontrol edin: -- Kod herhangi bir hata oluşturmuyor -- Kodunuz herhangi bir testi bozmaz. -- Kod değişikliğiniz test edilmiştir. -- Gereksiz beyaz alan değişiklikleri yapmıyorsunuz. +Taahhüt Açıklaması .[#toc-commit-description] +============================================= -Taahhüt mesajı aşağıdaki formata uygun olmalıdır `Latte: fixed multi template rendering [Closes # 69]` Yani: -- iki nokta üst üste ile takip edilen bir alan -- commit'in geçmişteki amacı, mümkünse "added.", "fixed.", "refactored.", changed, removed" ile başlayın -- sorun izleyiciye nihai bağlantı -- taahhüt geriye dönük uyumluluğu iptal ederse, "BC break" ekleyin -- konudan sonra bir serbest satır ve forum bağlantıları da dahil olmak üzere daha ayrıntılı bir açıklama olabilir. +Nette'de taahhüt konuları aşağıdaki formata sahiptir: `Presenter: fixed AJAX detection [Closes #69]` +- alanı ve ardından iki nokta üst üste +- geçmiş zamanda commit'in amacı; mümkünse aşağıdaki gibi kelimelerle başlayın: "added .(yeni özellik)", "fixed .(düzeltme)", "refactored .(davranış değişikliği olmadan kod değişikliği)", changed, removed +- eğer taahhüt geriye dönük uyumluluğu bozuyorsa, "BC break" ekleyin +- sorun izleyiciyle herhangi bir bağlantı, örneğin `(#123)` veya `[Closes #69]` +- konudan sonra bir boş satır ve ardından, örneğin forum bağlantıları da dahil olmak üzere, daha ayrıntılı bir açıklama gelebilir -Komiteleri Çekme-Talep Etme .[#toc-pull-requesting-the-commits] -=============================================================== -Kod değişikliklerinizden ve taahhütlerinizden memnunsanız, taahhütlerinizi GitHub'a göndermeniz gerekir. +Çekme Talebi Açıklaması .[#toc-pull-request-description] +======================================================== -```shell -git push origin new_branch_name -``` +Bir çekme isteği oluştururken, GitHub arayüzü bir başlık ve açıklama girmenize izin verecektir. Kısa ve öz bir başlık girin ve açıklamada değişikliğinizin nedenleri hakkında mümkün olduğunca fazla bilgi ekleyin. -Değişiklikler herkese açıktır, ancak değişikliklerinizi Nette'nin ana dalına entegre etmek için önermeniz gerekir. Bunu yapmak için [bir çekme isteği |https://help.github.com/articles/creating-a-pull-request] oluşturun. -Her çekme isteğinin bir başlığı ve bir açıklaması vardır. Lütfen açıklayıcı bir başlık girin. Genellikle şube adına benzer, örneğin "CSRF saldırısına karşı sinyalleri güvence altına almak." +Ayrıca, başlıkta yeni bir özellik mi yoksa bir hata düzeltmesi mi olduğunu ve geriye dönük uyumluluk sorunlarına (BC break) neden olup olmayacağını belirtin. İlgili bir sorun varsa, çekme isteğinin onaylanması üzerine kapatılması için ona bağlantı verin. -Çekme isteği açıklaması, kod değişiklikleriniz hakkında daha spesifik bilgiler içermelidir: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Lütfen bilgi tablosunu çekme talebinize uyacak şekilde değiştirin. Her liste öğesine yorumlar: -- Çekme isteğinin **özellik** mi eklediğini yoksa bir **hata düzeltme** mi olduğunu belirtir. -- Çekme isteği birleştirildikten sonra kapatılacak olan **ilgili soruna** atıfta bulunur. -- Çekme isteğinin **dokümantasyon değişikliklerine** ihtiyacı olup olmadığını söyler, evet ise, uygun çekme isteklerine referanslar sağlar. Dokümantasyon değişikliğini hemen sağlamak zorunda değilsiniz, ancak dokümantasyon değişikliğine ihtiyaç duyulursa çekme isteği birleştirilmeyecektir. Dokümantasyon değişikliği İngilizce dokümantasyon için hazırlanmalıdır, diğer dil mutasyonları isteğe bağlıdır. -- Çekme isteğinin **bir BC kırılması** yaratıp yaratmadığını söylüyor. Lütfen, genel arayüzü değiştiren her şeyi bir BC kırılması olarak düşünün. - -Final masası şöyle görünebilir: -``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no +- doc PR: nette/docs#? ``` -Değişikliklerinizin Yeniden Düzenlenmesi .[#toc-reworking-your-changes] -======================================================================= - -Kod değişikliğinize yorum almak gerçekten yaygın bir durumdur. Lütfen önerilen değişiklikleri takip etmeye çalışın ve bunu yapmak için taahhütlerinizi yeniden düzenleyin. Önerilen değişiklikleri yeni taahhütler olarak işleyebilir ve ardından bunları öncekilere sıkıştırabilirsiniz. GitHub'da [Etkileşimli |https://help.github.com/en/github/using-git/about-git-rebase] yeniden düzenleme bölümüne bakın. Değişikliklerinizi yeniden düzenledikten sonra, değişikliklerinizi uzak çatalınıza zorla itin, her şey otomatik olarak çekme isteğine yayılacaktır. - {{priority: -1}} diff --git a/contributing/uk/code.texy b/contributing/uk/code.texy index 3a2cca1145..0461320b30 100644 --- a/contributing/uk/code.texy +++ b/contributing/uk/code.texy @@ -1,110 +1,118 @@ -Пропонуємо внести зміни до Кодексу -********************************** +Внесок до Коду +************** -Nette Framework використовує Git та [GitHub |https://github.com/nette/nette] для підтримки кодової бази. Найкращий спосіб внести свій вклад - це зафіксувати зміни у власному форку, а потім зробити запит на витягування на GitHub. У цьому документі коротко описані основні кроки для успішного внесення змін. +.[perex] +Ви плануєте зробити свій внесок у Nette Framework і хочете ознайомитися з правилами та процедурами? Цей посібник для початківців допоможе вам зробити ефективний внесок у код, працювати з репозиторіями та впроваджувати зміни. -Підготовка середовища .[#toc-preparing-environment] -=================================================== +Порядок дій .[#toc-procedure] +============================= -Почніть з [розгалуження |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [Nette на GitHub |https://github.com/nette]. Уважно [налаштуйте |https://help.github.com/en/github/getting-started-with-github/set-up-git] локальне середовище Git'а, задайте ім'я користувача та електронну пошту, ці облікові дані будуть ідентифікувати ваші зміни в історії Nette Framework. +Щоб долучитися до коду, необхідно мати обліковий запис на [GitHub |https://github.com] і бути знайомим з основами роботи з системою контролю версій Git. Якщо ви не знайомі з Git'ом, ви можете ознайомитися з [git - простим керівництвом |https://rogerdudler.github.io/git-guide/] і розглянути можливість використання одного з багатьох [графічних клієнтів |https://git-scm.com/downloads/guis]. -Робота над вашим патчем .[#toc-working-on-your-patch] -===================================================== +Підготовка середовища та репозиторію .[#toc-preparing-the-environment-and-repository] +------------------------------------------------------------------------------------- + +1) На GitHub створіть [форк |https://help.github.com/en/github/getting-started-with-github/fork-a-repo] [сховища пакунків |www:packages], який ви збираєтеся модифікувати +2) [Клонуйте |https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository] цей репозиторій на свій комп'ютер +3) Встановіть залежності, включаючи [Nette Tester |tester:], за допомогою команди `composer install` +4) Переконайтеся, що тести працюють, запустивши їх `composer tester` +5) Створіть [нову гі |#New Branch] лку на основі останньої випущеної версії + + +Впровадження власних змін .[#toc-implementing-your-own-changes] +--------------------------------------------------------------- + +Тепер ви можете вносити власні корективи в код: + +1) Впроваджуйте бажані зміни і не забувайте про тести +2) Переконайтеся, що тести успішно запускаються за допомогою `composer tester` +3) Перевірте, чи відповідає код [стандартам кодування |#coding standards] +4) Збережіть (зафіксуйте) зміни з описом у [такому форматі |#Commit Description] + +Ви можете створити декілька комітів, по одному для кожного логічного кроку. Кожен коміт повинен бути значущим сам по собі. -Перш ніж почати роботу над патчем, створіть нову гілку для своїх змін. -```shell -git checkout -b new_branch_name -``` -Ви можете працювати над зміною коду. +Подання змін .[#toc-submitting-changes] +--------------------------------------- -Якщо можливо, вносьте зміни з останньої випущеної версії. +Якщо ви задоволені змінами, ви можете відправити їх: +1) Перенесіть зміни на GitHub у свій форк +2) Звідти відправте їх до репозиторію Nette, створивши [pull request|https://help.github.com/articles/creating-a-pull-request] (PR) +3) Надайте [достатньо інформації |#pull request description] в описі -Тестування ваших змін .[#toc-testing-your-changes] -================================================== -Вам потрібно встановити Nette Tester. Найпростіший спосіб - викликати `composer install` в корені сховища. Тепер ви зможете запускати тести за допомогою `./vendor/bin/tester` в терміналі. +Включення зворотного зв'язку .[#toc-incorporating-feedback] +----------------------------------------------------------- -Деякі тести можуть не працювати через відсутність php.ini. Тому вам слід викликати бігун з параметром -c і вказати шлях до php.ini, наприклад `./vendor/bin/tester -c ./tests/php.ini`. +Ваші комміти тепер видимі для інших. Ми часто отримуємо коментарі з пропозиціями: -Після того, як ви зможете запустити тести, ви можете реалізувати свої власні або змінити непрацюючі, щоб вони відповідали новій поведінці. Детальніше про тестування за допомогою Nette Tester читайте на [сторінці документації |tester:]. +1) Відстежуйте запропоновані зміни +2) Включіть їх як нові комміти або об'єднайте [з попередніми |https://help.github.com/en/github/using-git/about-git-rebase] +3) Повторно надішліть коміти на GitHub, і вони автоматично з'являться в запиті на витягування + +Ніколи не створюйте новий пул-запит для зміни існуючого. + + +Документація .[#toc-documentation] +---------------------------------- + +Якщо ви змінили функціонал або додали новий, не забудьте [додати його до документації |documentation]. + + +Нова гілка .[#toc-new-branch] +============================= + +Якщо можливо, вносьте зміни відповідно до останньої випущеної версії, тобто останнього тегу у гілці. Для тегу v3.2.1 створіть гілку за допомогою цієї команди: + +```shell +git checkout -b new_branch_name v3.2.1 +``` Стандарти кодування .[#toc-coding-standards] ============================================ -Ваш код повинен відповідати [стандартам кодування |coding standard], що використовуються в Nette Framework. Це легко зробити завдяки автоматизованій перевірці та виправленню помилок. Він вимагає PHP 7.1 і може бути встановлений через Composer у вибрану вами глобальну директорію: +Ваш код повинен відповідати [стандарту кодування |coding standard], що використовується в Nette Framework. Існує автоматичний інструмент для перевірки та виправлення коду. Ви можете встановити його **глобально** через Composer до папки на ваш вибір: ```shell composer create-project nette/coding-standard /path/to/nette-coding-standard ``` -Тепер ви зможете запустити інструмент у терміналі. Наприклад, ця команда перевіряє і виправляє код у теках `src` і `tests` у поточному каталозі: +Тепер ви зможете запустити інструмент у терміналі. Перша команда перевіряє, а друга - виправляє код у теках `src` і `tests` у поточному каталозі: ```shell -/path/to/nette-coding-standard/ecs check src tests --config /path/to/nette-coding-standard/coding-standard-php71.yml --fix +/path/to/nette-coding-standard/ecs check +/path/to/nette-coding-standard/ecs check --fix ``` -Фіксація змін .[#toc-committing-the-changes] -============================================ +Опис комміту .[#toc-commit-description] +======================================= -Після того, як ви змінили код, ви повинні зафіксувати зміни. Створіть більше коммітів, по одному для кожного логічного кроку. Кожен комміт має бути придатним для використання як є - без інших коммітів. Отже, відповідні тести також повинні бути включені в той самий комміт. +У Nette теми коммітів мають наступний формат: `Presenter: fixed AJAX detection [Closes #69]` -Будь ласка, перевірте, чи відповідає ваш код правилам: -- Код не генерує жодних помилок -- Ваш код не порушує жодного тесту. -- Ваша зміна коду протестована. -- Ви не вносите непотрібні зміни з пробілами. - -Повідомлення про фіксацію має відповідати формату `Latte: fixed multi template rendering [Closes # 69]` тобто - область, за якою слідує двокрапка -- мета комміту в минулому, якщо можливо, починайте зі слів "додано", "виправлено", "перероблено", "змінено", "вилучено -- можливе посилання на трекер випусків -- якщо комміт скасовує зворотну сумісність, додайте "BC break" -- після теми може бути один вільний рядок і більш детальний опис, включаючи посилання на форум. - +- мета комміту в минулому часі; якщо можливо, починайте зі слів на кшталт added, fixed, refactored, changed, removed +- якщо комміт порушує зворотну сумісність, додайте "BC break" +- будь-яке з'єднання з трекером випусків, наприклад, `(#123)` або `[Closes #69]` +- після теми може бути один порожній рядок, за яким слідує більш детальний опис, включаючи, наприклад, посилання на форум -Витягнення запиту на комміти .[#toc-pull-requesting-the-commits] -================================================================ -Якщо ви задоволені змінами в коді та комітами, вам потрібно відправити їх на GitHub. +Опис запиту на витягування .[#toc-pull-request-description] +=========================================================== -```shell -git push origin new_branch_name -``` +При створенні pull request інтерфейс GitHub дозволить вам ввести заголовок і опис. Надайте лаконічний заголовок і включіть якомога більше інформації в опис про причини вашої зміни. -Зміни присутні у відкритому доступі, однак, ви повинні запропонувати свої зміни для інтеграції в основну гілку Nette. Для цього створіть [pull request |https://help.github.com/articles/creating-a-pull-request]. -Кожен pull request має назву та опис. Будь ласка, надайте якийсь описовий заголовок. Часто вона схожа на назву гілки, наприклад, "Захист сигналів від CSRF-атаки". +Також вкажіть у заголовку, чи це нова функція, чи виправлення помилки, і чи може це спричинити проблеми зворотної сумісності (BC break). Якщо є пов'язана проблема, зробіть посилання на неї, щоб вона була закрита після схвалення запиту на вилучення. -В описі запиту слід вказати більш конкретну інформацію про зміни, які ви вносите до коду: ``` -- bug fix? yes/no -- new feature? yes/no +- bug fix / new feature? - BC break? yes/no -- doc PR: nette/docs#??? -``` - -Будь ласка, змініть інформаційну таблицю відповідно до вашого pull-запиту. Коментарі до кожного пункту списку: -- Зазначає, чи запит додає **можливість**, чи це **виправлення помилки**. -- Вказує на **пов'язану проблему**, яка буде закрита після об'єднання запиту. -- Показує, чи потребує запит **зміни документації**, якщо так, надайте посилання на відповідні запити. Вам не обов'язково надавати зміни до документації негайно, однак, якщо вони потрібні, запит не буде об'єднано. Зміна документації повинна бути підготовлена для англійської документації, інші мовні мутації не є обов'язковими. -- Каже, якщо pull request створює **розрив BC**. Будь ласка, розглядайте все, що змінює публічний інтерфейс, як розрив BC. - -Фінальна таблиця може виглядати так: +- doc PR: nette/docs#? ``` -- bug fix? no -- new feature? yes issue #123 -- BC break? no -``` - - -Переробляючи свої зміни .[#toc-reworking-your-changes] -====================================================== -Дуже часто ми отримуємо коментарі до змін у коді. Будь ласка, намагайтеся слідувати запропонованим змінам і переробляти свої коміти для цього. Ви можете зафіксувати запропоновані зміни як нові коміти, а потім витіснити їх до попередніх. Дивіться розділ [Інтерактивне |https://help.github.com/en/github/using-git/about-git-rebase] відновлення на GitHub. Після ребазирования виконайте примусове перенесення змін у віддалений форк, і все буде автоматично поширено в пул-запит. {{priority: -1}} From bd86fe9657b17713750f6d7605405e02f324c7bd Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 14 Mar 2023 21:48:06 +0100 Subject: [PATCH 07/47] presenter Homepage -> Home --- application/bg/ajax.texy | 2 +- application/bg/creating-links.texy | 14 ++++++------ application/bg/how-it-works.texy | 10 ++++----- application/bg/modules.texy | 4 ++-- application/bg/routing.texy | 34 +++++++++++++++--------------- application/bg/templates.texy | 2 +- application/cs/ajax.texy | 2 +- application/cs/creating-links.texy | 16 +++++++------- application/cs/how-it-works.texy | 10 ++++----- application/cs/modules.texy | 4 ++-- application/cs/routing.texy | 34 +++++++++++++++--------------- application/cs/templates.texy | 2 +- application/de/ajax.texy | 2 +- application/de/creating-links.texy | 16 +++++++------- application/de/how-it-works.texy | 10 ++++----- application/de/modules.texy | 4 ++-- application/de/routing.texy | 34 +++++++++++++++--------------- application/de/templates.texy | 2 +- application/el/ajax.texy | 2 +- application/el/creating-links.texy | 16 +++++++------- application/el/how-it-works.texy | 10 ++++----- application/el/modules.texy | 4 ++-- application/el/routing.texy | 34 +++++++++++++++--------------- application/el/templates.texy | 2 +- application/en/ajax.texy | 2 +- application/en/creating-links.texy | 16 +++++++------- application/en/how-it-works.texy | 10 ++++----- application/en/modules.texy | 4 ++-- application/en/routing.texy | 34 +++++++++++++++--------------- application/en/templates.texy | 2 +- application/es/ajax.texy | 2 +- application/es/creating-links.texy | 16 +++++++------- application/es/how-it-works.texy | 10 ++++----- application/es/modules.texy | 4 ++-- application/es/routing.texy | 34 +++++++++++++++--------------- application/es/templates.texy | 2 +- application/fr/ajax.texy | 2 +- application/fr/creating-links.texy | 16 +++++++------- application/fr/how-it-works.texy | 10 ++++----- application/fr/modules.texy | 4 ++-- application/fr/routing.texy | 34 +++++++++++++++--------------- application/fr/templates.texy | 2 +- application/hu/ajax.texy | 2 +- application/hu/creating-links.texy | 16 +++++++------- application/hu/how-it-works.texy | 10 ++++----- application/hu/modules.texy | 4 ++-- application/hu/routing.texy | 34 +++++++++++++++--------------- application/hu/templates.texy | 2 +- application/it/ajax.texy | 2 +- application/it/creating-links.texy | 16 +++++++------- application/it/how-it-works.texy | 10 ++++----- application/it/modules.texy | 4 ++-- application/it/routing.texy | 34 +++++++++++++++--------------- application/it/templates.texy | 2 +- application/pl/ajax.texy | 2 +- application/pl/creating-links.texy | 16 +++++++------- application/pl/how-it-works.texy | 10 ++++----- application/pl/modules.texy | 4 ++-- application/pl/routing.texy | 34 +++++++++++++++--------------- application/pl/templates.texy | 2 +- application/pt/ajax.texy | 2 +- application/pt/creating-links.texy | 16 +++++++------- application/pt/how-it-works.texy | 10 ++++----- application/pt/modules.texy | 4 ++-- application/pt/routing.texy | 34 +++++++++++++++--------------- application/pt/templates.texy | 2 +- application/ro/ajax.texy | 2 +- application/ro/creating-links.texy | 16 +++++++------- application/ro/how-it-works.texy | 10 ++++----- application/ro/modules.texy | 4 ++-- application/ro/routing.texy | 34 +++++++++++++++--------------- application/ro/templates.texy | 2 +- application/ru/ajax.texy | 2 +- application/ru/creating-links.texy | 14 ++++++------ application/ru/how-it-works.texy | 10 ++++----- application/ru/modules.texy | 4 ++-- application/ru/routing.texy | 34 +++++++++++++++--------------- application/ru/templates.texy | 2 +- application/sl/ajax.texy | 2 +- application/sl/creating-links.texy | 16 +++++++------- application/sl/how-it-works.texy | 10 ++++----- application/sl/modules.texy | 4 ++-- application/sl/routing.texy | 34 +++++++++++++++--------------- application/sl/templates.texy | 2 +- application/tr/ajax.texy | 2 +- application/tr/creating-links.texy | 16 +++++++------- application/tr/how-it-works.texy | 10 ++++----- application/tr/modules.texy | 4 ++-- application/tr/routing.texy | 32 ++++++++++++++-------------- application/tr/templates.texy | 2 +- application/uk/ajax.texy | 2 +- application/uk/creating-links.texy | 14 ++++++------ application/uk/how-it-works.texy | 10 ++++----- application/uk/modules.texy | 4 ++-- application/uk/routing.texy | 34 +++++++++++++++--------------- application/uk/templates.texy | 2 +- best-practices/bg/pagination.texy | 8 +++---- best-practices/cs/pagination.texy | 8 +++---- best-practices/de/pagination.texy | 8 +++---- best-practices/el/pagination.texy | 8 +++---- best-practices/en/pagination.texy | 8 +++---- best-practices/es/pagination.texy | 8 +++---- best-practices/fr/pagination.texy | 8 +++---- best-practices/hu/pagination.texy | 8 +++---- best-practices/it/pagination.texy | 8 +++---- best-practices/pl/pagination.texy | 8 +++---- best-practices/pt/pagination.texy | 6 +++--- best-practices/ro/pagination.texy | 8 +++---- best-practices/ru/pagination.texy | 8 +++---- best-practices/sl/pagination.texy | 8 +++---- best-practices/tr/pagination.texy | 8 +++---- best-practices/uk/pagination.texy | 8 +++---- forms/bg/in-presenter.texy | 8 +++---- forms/bg/validation.texy | 2 +- forms/cs/in-presenter.texy | 8 +++---- forms/cs/validation.texy | 2 +- forms/de/in-presenter.texy | 8 +++---- forms/de/validation.texy | 2 +- forms/el/in-presenter.texy | 8 +++---- forms/el/validation.texy | 2 +- forms/en/in-presenter.texy | 8 +++---- forms/en/validation.texy | 2 +- forms/es/in-presenter.texy | 8 +++---- forms/es/validation.texy | 2 +- forms/fr/in-presenter.texy | 8 +++---- forms/fr/validation.texy | 2 +- forms/hu/in-presenter.texy | 8 +++---- forms/hu/validation.texy | 2 +- forms/it/in-presenter.texy | 8 +++---- forms/it/validation.texy | 2 +- forms/pl/in-presenter.texy | 6 +++--- forms/pt/in-presenter.texy | 8 +++---- forms/pt/validation.texy | 2 +- forms/ro/in-presenter.texy | 8 +++---- forms/ro/validation.texy | 2 +- forms/ru/in-presenter.texy | 8 +++---- forms/ru/validation.texy | 2 +- forms/sl/in-presenter.texy | 8 +++---- forms/sl/validation.texy | 2 +- forms/tr/in-presenter.texy | 8 +++---- forms/tr/validation.texy | 2 +- forms/uk/in-presenter.texy | 8 +++---- forms/uk/validation.texy | 2 +- latte/bg/template-inheritance.texy | 2 +- latte/cs/template-inheritance.texy | 2 +- latte/de/template-inheritance.texy | 2 +- latte/el/template-inheritance.texy | 2 +- latte/en/template-inheritance.texy | 2 +- latte/es/template-inheritance.texy | 2 +- latte/fr/template-inheritance.texy | 2 +- latte/hu/template-inheritance.texy | 2 +- latte/it/template-inheritance.texy | 2 +- latte/ja/template-inheritance.texy | 2 +- latte/pl/template-inheritance.texy | 2 +- latte/pt/template-inheritance.texy | 2 +- latte/ro/template-inheritance.texy | 2 +- latte/ru/template-inheritance.texy | 2 +- latte/sl/template-inheritance.texy | 2 +- latte/tr/template-inheritance.texy | 2 +- latte/uk/template-inheritance.texy | 2 +- quickstart/bg/@home.texy | 4 ++-- quickstart/bg/authentication.texy | 8 +++---- quickstart/bg/creating-posts.texy | 2 +- quickstart/bg/home-page.texy | 18 ++++++++-------- quickstart/bg/model.texy | 8 +++---- quickstart/bg/single-post.texy | 12 +++++------ quickstart/cs/@home.texy | 4 ++-- quickstart/cs/authentication.texy | 8 +++---- quickstart/cs/creating-posts.texy | 2 +- quickstart/cs/home-page.texy | 18 ++++++++-------- quickstart/cs/model.texy | 8 +++---- quickstart/cs/single-post.texy | 12 +++++------ quickstart/de/@home.texy | 4 ++-- quickstart/de/authentication.texy | 8 +++---- quickstart/de/creating-posts.texy | 2 +- quickstart/de/home-page.texy | 18 ++++++++-------- quickstart/de/model.texy | 8 +++---- quickstart/de/single-post.texy | 12 +++++------ quickstart/el/@home.texy | 4 ++-- quickstart/el/authentication.texy | 8 +++---- quickstart/el/creating-posts.texy | 2 +- quickstart/el/home-page.texy | 18 ++++++++-------- quickstart/el/model.texy | 8 +++---- quickstart/el/single-post.texy | 12 +++++------ quickstart/en/@home.texy | 4 ++-- quickstart/en/authentication.texy | 8 +++---- quickstart/en/creating-posts.texy | 2 +- quickstart/en/home-page.texy | 18 ++++++++-------- quickstart/en/model.texy | 8 +++---- quickstart/en/single-post.texy | 12 +++++------ quickstart/es/@home.texy | 4 ++-- quickstart/es/authentication.texy | 8 +++---- quickstart/es/creating-posts.texy | 2 +- quickstart/es/home-page.texy | 18 ++++++++-------- quickstart/es/model.texy | 8 +++---- quickstart/es/single-post.texy | 12 +++++------ quickstart/fr/@home.texy | 4 ++-- quickstart/fr/authentication.texy | 8 +++---- quickstart/fr/creating-posts.texy | 2 +- quickstart/fr/home-page.texy | 18 ++++++++-------- quickstart/fr/model.texy | 8 +++---- quickstart/fr/single-post.texy | 12 +++++------ quickstart/hu/@home.texy | 4 ++-- quickstart/hu/authentication.texy | 8 +++---- quickstart/hu/creating-posts.texy | 2 +- quickstart/hu/home-page.texy | 18 ++++++++-------- quickstart/hu/model.texy | 8 +++---- quickstart/hu/single-post.texy | 12 +++++------ quickstart/it/@home.texy | 4 ++-- quickstart/it/authentication.texy | 8 +++---- quickstart/it/creating-posts.texy | 2 +- quickstart/it/home-page.texy | 18 ++++++++-------- quickstart/it/model.texy | 8 +++---- quickstart/it/single-post.texy | 12 +++++------ quickstart/pl/@home.texy | 4 ++-- quickstart/pl/authentication.texy | 8 +++---- quickstart/pl/creating-posts.texy | 2 +- quickstart/pl/home-page.texy | 18 ++++++++-------- quickstart/pl/model.texy | 8 +++---- quickstart/pl/single-post.texy | 12 +++++------ quickstart/pt/@home.texy | 4 ++-- quickstart/pt/authentication.texy | 8 +++---- quickstart/pt/creating-posts.texy | 2 +- quickstart/pt/home-page.texy | 18 ++++++++-------- quickstart/pt/model.texy | 8 +++---- quickstart/pt/single-post.texy | 12 +++++------ quickstart/ro/@home.texy | 4 ++-- quickstart/ro/authentication.texy | 8 +++---- quickstart/ro/creating-posts.texy | 2 +- quickstart/ro/home-page.texy | 18 ++++++++-------- quickstart/ro/model.texy | 8 +++---- quickstart/ro/single-post.texy | 12 +++++------ quickstart/ru/@home.texy | 4 ++-- quickstart/ru/authentication.texy | 8 +++---- quickstart/ru/creating-posts.texy | 2 +- quickstart/ru/home-page.texy | 18 ++++++++-------- quickstart/ru/model.texy | 8 +++---- quickstart/ru/single-post.texy | 12 +++++------ quickstart/sl/@home.texy | 4 ++-- quickstart/sl/authentication.texy | 8 +++---- quickstart/sl/creating-posts.texy | 2 +- quickstart/sl/home-page.texy | 18 ++++++++-------- quickstart/sl/model.texy | 8 +++---- quickstart/sl/single-post.texy | 12 +++++------ quickstart/tr/@home.texy | 4 ++-- quickstart/tr/authentication.texy | 8 +++---- quickstart/tr/creating-posts.texy | 2 +- quickstart/tr/home-page.texy | 18 ++++++++-------- quickstart/tr/model.texy | 8 +++---- quickstart/tr/single-post.texy | 12 +++++------ quickstart/uk/@home.texy | 4 ++-- quickstart/uk/authentication.texy | 8 +++---- quickstart/uk/creating-posts.texy | 2 +- quickstart/uk/home-page.texy | 18 ++++++++-------- quickstart/uk/model.texy | 8 +++---- quickstart/uk/single-post.texy | 12 +++++------ tracy/bg/guide.texy | 2 +- tracy/cs/guide.texy | 2 +- tracy/de/guide.texy | 2 +- tracy/el/guide.texy | 2 +- tracy/en/guide.texy | 2 +- tracy/es/guide.texy | 2 +- tracy/fr/guide.texy | 2 +- tracy/hu/guide.texy | 2 +- tracy/it/guide.texy | 2 +- tracy/pl/guide.texy | 2 +- tracy/pt/guide.texy | 2 +- tracy/ro/guide.texy | 2 +- tracy/ru/guide.texy | 2 +- tracy/sl/guide.texy | 2 +- tracy/tr/guide.texy | 2 +- tracy/uk/guide.texy | 2 +- 272 files changed, 1130 insertions(+), 1130 deletions(-) diff --git a/application/bg/ajax.texy b/application/bg/ajax.texy index 5ac48a22b8..924c419cf7 100644 --- a/application/bg/ajax.texy +++ b/application/bg/ajax.texy @@ -149,7 +149,7 @@ $this->isControlInvalid('footer'); // -> true В горния пример трябва да се уверите, че само един елемент ще бъде добавен към масива `$list`, когато бъде направена заявката AJAX, така че цикълът `foreach` ще изведе само един динамичен фрагмент. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * Этот метод возвращает данные для списка. diff --git a/application/bg/creating-links.texy b/application/bg/creating-links.texy index 6097a3719c..659326e6fe 100644 --- a/application/bg/creating-links.texy +++ b/application/bg/creating-links.texy @@ -52,7 +52,7 @@ Атрибутът `n:href` е много полезен за HTML тагове. ``. Ако искаме да покажем връзката на друго място, например в текста, използваме `{link}`: ```latte -URL: {link Homepage:default} +URL: {link Home:default} ``` @@ -88,7 +88,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Следователно основната форма е `Presenter:action`: ```latte -главная страница +главная страница ``` Ако се позоваваме на действието на текущия водещ, можем да пропуснем името му: @@ -100,7 +100,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Ако действието е `default`, можем да го пропуснем, но двоеточието трябва да остане: ```latte -главная страница +главная страница ``` Връзките могат да сочат и към други [модули |modules]. Тук връзките се разграничават на относителни към подмодули или абсолютни. Принципът е подобен на дисковите пътища, само че с двоеточия вместо с наклонени черти. Да предположим, че водещият е част от модул `Front`, тогава записваме: @@ -119,7 +119,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Можем да направим връзка към определена част от HTML страницата чрез така наречения фрагмент след символа хеш `#`: ```latte -ссылка на Homepage:default и фрагмент #main +ссылка на Home:default и фрагмент #main ``` @@ -128,7 +128,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Връзките, генерирани от `link()` или `n:href`, винаги са абсолютни пътища (т.е. започват с `/`), но не и абсолютни URL адреси с протокол и домейн, като `https://domain`. -За да създадете абсолютен URL адрес, добавете две наклонени черти в началото (например `n:href="//Homepage:"`). Или можете да превключите презентатора да генерира само абсолютни връзки, като зададете `$this->absoluteUrls = true`. +За да създадете абсолютен URL адрес, добавете две наклонени черти в началото (например `n:href="//Home:"`). Или можете да превключите презентатора да генерира само абсолютни връзки, като зададете `$this->absoluteUrls = true`. Връзка към текущата страница .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Ако искаме да направим препратка към презентаторите в шаблона на компонента, използваме тага `{plink}`: ```latte -главная страница +главная страница ``` или в кода ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/bg/how-it-works.texy b/application/bg/how-it-works.texy index b329738fcc..6f267f3b26 100644 --- a/application/bg/how-it-works.texy +++ b/application/bg/how-it-works.texy @@ -23,10 +23,10 @@ web-project/ ├── app/ ← каталог с приложением │ ├── Presenters/ ← классы презентеров -│ │ ├── HomepagePresenter.php ← Класс презентера главной страницы +│ │ ├── HomePresenter.php ← Класс презентера главной страницы │ │ └── templates/ ← директория шаблонов │ │ ├── @layout.latte ← шаблон общего макета -│ │ └── Homepage/ ← шаблоны презентера главной страницы +│ │ └── Home/ ← шаблоны презентера главной страницы │ │ └── default.latte ← шаблон действия `default` │ ├── Router/ ← конфигурация URL-адресов │ └── Bootstrap.php ← загрузочный класс Bootstrap @@ -134,10 +134,10 @@ class ProductPresenter extends Nette\Application\UI\Presenter 1) URL адресът ще `https://example.com` 2) изтегляме приложението, създаваме контейнер и стартираме `Application::run()` -3) маршрутизаторът декодира URL адреса като двойка `Homepage:default` -4) обектът е създаден `HomepagePresenter` +3) маршрутизаторът декодира URL адреса като двойка `Home:default` +4) обектът е създаден `HomePresenter` 5) извиква се методът `renderDefault()` (ако съществува) -6) шаблонът `templates/Homepage/default.latte` с оформлението `templates/@layout.latte` се визуализира +6) шаблонът `templates/Home/default.latte` с оформлението `templates/@layout.latte` се визуализира Може би сега ще се сблъскате с много нови концепции, но ние смятаме, че те имат смисъл. Създаването на приложения в Nette е лесно. diff --git a/application/bg/modules.texy b/application/bg/modules.texy index d62f08c7d5..622fd48b2e 100644 --- a/application/bg/modules.texy +++ b/application/bg/modules.texy @@ -104,7 +104,7 @@ class DashboardPresenter extends Nette\Application\UI\Presenter Определя правилата, по които името на класа се извежда от главното име. Записваме ги в [конфигурацията |configuration] под ключа `application › mapping`. -Нека започнем с пример, при който не се използват модули. Искаме само главните класове да имат пространството от имена `App\Presenters`. Това означава, че искаме главното име, например `Homepage`, да се съпостави с класа `App\Presenters\HomepagePresenter`. Това може да се постигне със следната конфигурация: +Нека започнем с пример, при който не се използват модули. Искаме само главните класове да имат пространството от имена `App\Presenters`. Това означава, че искаме главното име, например `Home`, да се съпостави с класа `App\Presenters\HomePresenter`. Това може да се постигне със следната конфигурация: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Сега водещият `Front:Homepage` е определен от класа `App\Modules\Front\HomepagePresenter`, а презентер `Admin:Dashboard` - `App\AdminModule\DashboardPresenter`. +Сега водещият `Front:Home` е определен от класа `App\Modules\Front\HomePresenter`, а презентер `Admin:Dashboard` - `App\AdminModule\DashboardPresenter`. Би било по-удобно да се създаде общо правило (звездичка), което да замени първите две правила, и да се добави допълнителна звездичка само за модула: diff --git a/application/bg/routing.texy b/application/bg/routing.texy index 183446edbb..96e134bc79 100644 --- a/application/bg/routing.texy +++ b/application/bg/routing.texy @@ -93,12 +93,12 @@ $router->addRoute('chronicle/', 'History:show'); Разбира се, името на водещия и действието също могат да бъдат параметри. Например: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Този маршрут приема например URL адреси под формата съответно на `/article/edit` и `/catalog/list` и ги превръща в презентатори и действия съответно `Article:edit` и `Catalog:list`. -Той също така дава на `presenter` и `action` стойности по подразбиране за `Homepage` и `default` и следователно те не са задължителни. Затова маршрутът също така взема URL адреса `/article` и го превежда като `Article:default`. Или обратното, връзка към `Product:default` генерира пътя `/product`, а връзка към стандартния `Homepage:default` генерира пътя `/`. +Той също така дава на `presenter` и `action` стойности по подразбиране за `Home` и `default` и следователно те не са задължителни. Затова маршрутът също така взема URL адреса `/article` и го превежда като `Article:default`. Или обратното, връзка към `Product:default` генерира пътя `/product`, а връзка към стандартния `Home:default` генерира пътя `/`. Маската може да описва не само относителен път, базиран на корена на сайта, но и абсолютен път, ако започва с наклонена черта, или дори цял абсолютен URL адрес, ако започва с две наклонени черти: @@ -160,7 +160,7 @@ $router->addRoute('//[.]example.com//', /* ... */); ```php $router->addRoute( '[[-]/][page-]', - 'Homepage:default', + 'Home:default', ); //получени URL адреси: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Незадължителни параметри (т.е. параметри, които имат стойност по подразбиране) без квадратни скоби се държат така, сякаш са обвити по този начин: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // е равно на: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -За да промените начина, по който се генерира най-дясната наклонена черта, т.е. вместо `/homepage/` да получите `/homepage`, конфигурирайте маршрута по този начин: +За да промените начина, по който се генерира най-дясната наклонена черта, т.е. вместо `/home/` да получите `/home`, конфигурирайте маршрута по този начин: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ $router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ $router->addRoute('/[/]', [ Добра практика е изходният код да бъде написан на английски език, но какво да правите, ако URL адресът на уебсайта ви трябва да бъде преведен на друг език? ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` ще генерира английски URL адреси като `/product/123` или `/cart`. Ако искаме презентаторите и действията в URL адреса да бъдат преведени на немски език (напр. `/produkt/123` или `/einkaufswagen`), можем да използваме речник за превод. За да го добавим, вече се нуждаем от "по-ясна" версия на втория параметър: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // строка в URL => ведущий 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ $router->addRoute('//', [ use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Параметърът на конструктора `SimpleRouter` е презентаторът и действието по подразбиране, т.е. действието, което ще се изпълни, ако отворим например `http://example.com/` без никакви допълнителни параметри. ```php -// използвайте презентатора 'Homepage' и действието 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// използвайте презентатора 'Home' и действието 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Препоръчваме да дефинирате SimpleRouter директно в [конфигурацията |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ class MyRouter implements Nette\Routing\Router ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/bg/templates.texy b/application/bg/templates.texy index d32c30c5ba..05a8985093 100644 --- a/application/bg/templates.texy +++ b/application/bg/templates.texy @@ -148,7 +148,7 @@ public function renderDefault(): void Атрибутът `n:href` е много удобен за HTML таговете. ``. Ако искаме да посочим връзка на друго място, например в текста, използваме `{link}`: ```latte -Adresa je: {link Homepage:default} +Adresa je: {link Home:default} ``` Вижте [Създаване на URL връзки |creating-links] за повече информация. diff --git a/application/cs/ajax.texy b/application/cs/ajax.texy index 90b0c2d742..ac3792e0b6 100644 --- a/application/cs/ajax.texy +++ b/application/cs/ajax.texy @@ -149,7 +149,7 @@ Dynamické snippety nelze invalidovat přímo (invalidace `item-1` neudělá vů V příkladu výše zkrátka musíte zajistit, aby při ajaxovém požadavku byla v proměnné `$list` pouze jedna položka a tedy aby ten cyklus `foreach` naplnil pouze jeden dynamický snippet: ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * Tato metoda vrací data pro seznam. diff --git a/application/cs/creating-links.texy b/application/cs/creating-links.texy index b2f3fcf88d..a21c986098 100644 --- a/application/cs/creating-links.texy +++ b/application/cs/creating-links.texy @@ -52,7 +52,7 @@ V odkazech se také automaticky předávají tzv. [persistentní parametry|prese Atribut `n:href` je velmi šikovný pro HTML značky ``. Chceme-li odkaz vypsat jinde, například v textu, použijeme `{link}`: ```latte -Adresa je: {link Homepage:default} +Adresa je: {link Home:default} ``` @@ -88,7 +88,7 @@ Formát podporují všechny značky Latte a všechny metody presenteru, které s Základním tvarem je tedy `Presenter:action`: ```latte -úvodní stránka +úvodní stránka ``` Pokud odkazujeme na akci aktuálního presenteru, můžeme jeho název vynechat: @@ -100,7 +100,7 @@ Pokud odkazujeme na akci aktuálního presenteru, můžeme jeho název vynechat: Pokud je cílem akce `default`, můžeme ji vynechat, ale dvojtečka musí zůstat: ```latte -úvodní stránka +ú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: @@ -119,7 +119,7 @@ Speciálním případem je odkaz [na sebe sama|#Odkaz na aktuální stránku], k Odkazovat můžeme na určitou část stránky přes tzv. fragment za znakem mřížky `#`: ```latte -odkaz na Homepage:default a fragment #main +odkaz na Home:default a fragment #main ``` @@ -128,7 +128,7 @@ Absolutní cesty Odkazy generované pomocí `link()` nebo `n:href` jsou vždy absolutní cesty (tj. začínají znakem `/`), ale nikoliv absolutní URL s protokolem a doménou jako `https://domain`. -Pro vygenerování absolutní URL přidejte na začátek dvě lomítka (např. `n:href="//Homepage:"`). Nebo lze přepnout presenter, aby generoval jen absolutní odkazy nastavením `$this->absoluteUrls = true`. +Pro vygenerování absolutní URL přidejte na začátek dvě lomítka (např. `n:href="//Home:"`). Nebo lze přepnout presenter, aby generoval jen absolutní odkazy nastavením `$this->absoluteUrls = true`. Odkaz na aktuální stránku @@ -165,7 +165,7 @@ Parametry jsou stejné jako u metody `link()`, navíc je však možné místo ko V kombinaci s `n:href` v jednom elementu se dá použít zkrácená podoba: ```latte -... +... ``` Zástupný znak `*` lze použít pouze místo akce, nikoliv presenteru. @@ -213,13 +213,13 @@ Protože [komponenty|components] jsou samostatné znovupoužitelné celky, kter Pokud bychom chtěli v šabloně komponenty odkazovat na presentery, použijeme k tomu značku `{plink}`: ```latte -úvod +úvod ``` nebo v kódu ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/cs/how-it-works.texy b/application/cs/how-it-works.texy index e696819c47..b0025a12a0 100644 --- a/application/cs/how-it-works.texy +++ b/application/cs/how-it-works.texy @@ -23,10 +23,10 @@ Adresářová struktura vypadá nějak takto: web-project/ ├── app/ ← adresář s aplikací │ ├── Presenters/ ← presentery a šablony -│ │ ├── HomepagePresenter.php ← třída presenteru Homepage +│ │ ├── HomePresenter.php ← třída presenteru Home │ │ └── templates/ ← adresář se šablonami │ │ ├── @layout.latte ← šablona layoutu -│ │ └── Homepage/ ← šablony presenteru Homepage +│ │ └── Home/ ← šablony presenteru Home │ │ └── default.latte ← šablona akce 'default' │ ├── Router/ ← konfigurace URL adres │ └── Bootstrap.php ← zaváděcí třída Bootstrap @@ -134,10 +134,10 @@ Pro jistotu, zkusme si zrekapitulovat celý proces s trošku jinou URL: 1) URL bude `https://example.com` 2) bootujeme aplikaci, vytvoří se kontejner a spustí `Application::run()` -3) router URL dekóduje jako dvojici `Homepage:default` -4) vytvoří se objekt třídy `HomepagePresenter` +3) router URL dekóduje jako dvojici `Home:default` +4) vytvoří se objekt třídy `HomePresenter` 5) zavolá se metoda `renderDefault()` (pokud existuje) -6) vykreslí se šablona např. `templates/Homepage/default.latte` s layoutem např. `templates/@layout.latte` +6) vykreslí se šablona např. `templates/Home/default.latte` s layoutem např. `templates/@layout.latte` Možná jste se teď setkali s velkou spoustou nových pojmů, ale věříme, že dávají smysl. Tvorba aplikací v Nette je ohromná pohodička. diff --git a/application/cs/modules.texy b/application/cs/modules.texy index d1158b7d2a..2cb3e42719 100644 --- a/application/cs/modules.texy +++ b/application/cs/modules.texy @@ -104,7 +104,7 @@ Mapování Definuje pravidla, podle kterých se z názvu presenteru odvodí název třídy. Zapisujeme je v [konfiguraci|configuration] pod klíčem `application › mapping`. -Začněme ukázkou, která moduly nepoužívá. Budeme jen chtít, aby třídy presenterů měly jmenný prostor `App\Presenters`. Tedy aby se presenter například `Homepage` mapoval na třídu `App\Presenters\HomepagePresenter`. Toho lze docílit následující konfigurací: +Začněme ukázkou, která moduly nepoužívá. Budeme jen chtít, aby třídy presenterů měly jmenný prostor `App\Presenters`. Tedy aby se presenter například `Home` mapoval na třídu `App\Presenters\HomePresenter`. Toho lze docílit následující konfigurací: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Nyní se presenter `Front:Homepage` mapuje na třídu `App\Modules\Front\Presenters\HomepagePresenter` a presenter `Admin:Dashboard` na třídu `App\Modules\Admin\Presenters\DashboardPresenter`. +Nyní se presenter `Front:Home` mapuje na třídu `App\Modules\Front\Presenters\HomePresenter` a presenter `Admin:Dashboard` na třídu `App\Modules\Admin\Presenters\DashboardPresenter`. Praktičtější bude vytvořit obecné (hvězdičkové) pravidlo, které první dvě nahradí. V masce třídy přibude hvezdička navíc právě pro modul: diff --git a/application/cs/routing.texy b/application/cs/routing.texy index aee02d66fe..816436fac4 100644 --- a/application/cs/routing.texy +++ b/application/cs/routing.texy @@ -93,12 +93,12 @@ Routa bude nyní akceptovat i URL `https://example.com/chronicle/`, které opět Parametrem může být samozřejmě i jméno presenteru a akce. Třeba takto: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Uvedená routa akceptuje např. URL ve tvaru `/article/edit` nebo také `/catalog/list` a chápe je jako presentery a akce `Article:edit` a `Catalog:list`. -Zaroveň dává parametrům `presenter` a `action` výchozí hodnoty `Homepage` a `default` a jsou tedy také volitelné. Takže routa akceptuje i URL ve tvaru `/article` a chápe ji jako `Article:default`. Nebo obráceně, odkaz na `Product:default` vygeneruje cestu `/product`, odkaz na výchozí `Homepage:default` cestu `/`. +Zaroveň dává parametrům `presenter` a `action` výchozí hodnoty `Home` a `default` a jsou tedy také volitelné. Takže routa akceptuje i URL ve tvaru `/article` a chápe ji jako `Article:default`. Nebo obráceně, odkaz na `Product:default` vygeneruje cestu `/product`, odkaz na výchozí `Home:default` cestu `/`. Maska může popisovat nejen relativní cestu od kořenového adresáře webu, ale také absolutní cestu, pokud začíná lomítkem, nebo dokonce celé absolutní URL, začíná-li dvěma lomítky: @@ -160,7 +160,7 @@ Sekvence je možné libovolně zanořovat a kombinovat: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Akceptuje cesty: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Volitelné parametry (tj. parametry mající výchozí hodnotu) bez hranatých závorek se chovají v podstatě tak, jako by byly uzávorkovány následujícím způsobem: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // odpovídá tomuto: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Pokud bychom chtěli ovlivnit chování koncového lomítka, aby se např. místo `/homepage/` generovalo jen `/homepage`, lze toho docílit takto: +Pokud bychom chtěli ovlivnit chování koncového lomítka, aby se např. místo `/home/` generovalo jen `/home`, lze toho docílit takto: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Druhý parametr routy, který často zapisujeme ve formátu `Presenter:action`, ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtry a překlady Zdrojové kódy aplikace píšeme v angličtině, ale pokud má mít web české URL, pak jednoduché routování typu: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` bude generovat anglické URL, jako třeba `/product/123` nebo `/cart`. Pokud chceme mít presentery a akce v URL reprezentované českými slovy (např. `/produkt/123` nebo `/kosik`), můžeme využít překladového slovníku. Pro jeho zápis už potřebujeme "upovídanější" variantu druhého parametru: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // řetězec v URL => presenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Vedle filtrů určených pro konkrétní parametry můžeme definovat též obec use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Parametrem konstruktoru SimpleRouteru je výchozí presenter & akce, na který se má směřovat, pokud otevřeme stránku bez parametrů, např. `http://example.com/`. ```php -// výchozím presenterem bude 'Homepage' a akce 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// výchozím presenterem bude 'Home' a akce 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Doporučujeme SimpleRouter přímo definovat v [konfiguraci |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Při zpracování požadavku musíme vrátit minimálně presenter a akci. Náze ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/cs/templates.texy b/application/cs/templates.texy index 075572e94e..3630e00ee4 100644 --- a/application/cs/templates.texy +++ b/application/cs/templates.texy @@ -148,7 +148,7 @@ V šabloně se vytvářejí odkazy na další presentery & akce tímto způsobem Atribut `n:href` je velmi šikovný pro HTML značky ``. Chceme-li odkaz vypsat jinde, například v textu, použijeme `{link}`: ```latte -Adresa je: {link Homepage:default} +Adresa je: {link Home:default} ``` Více informací najdete v kapitole [Vytváření odkazů URL|creating-links]. diff --git a/application/de/ajax.texy b/application/de/ajax.texy index a51e2b4011..bcdbd4ac93 100644 --- a/application/de/ajax.texy +++ b/application/de/ajax.texy @@ -149,7 +149,7 @@ Ein dynamisches Snippet kann nicht direkt neu gezeichnet werden (das erneute Zei Im obigen Beispiel müssen Sie sicherstellen, dass bei einer AJAX-Anfrage nur ein Element zum Array `$list` hinzugefügt wird, so dass die Schleife `foreach` nur ein dynamisches Snippet ausgibt. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/de/creating-links.texy b/application/de/creating-links.texy index ae9a95e4d6..1ec3fbd1e9 100644 --- a/application/de/creating-links.texy +++ b/application/de/creating-links.texy @@ -52,7 +52,7 @@ Die so genannten [persistenten Parameter |presenters#persistent parameters] werd Das Attribut `n:href` ist sehr praktisch für HTML-Tags ``. Wenn wir den Link an anderer Stelle, zum Beispiel im Text, ausgeben wollen, verwenden wir `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ Das Format wird von allen Latte-Tags und allen Presenter-Methoden unterstützt, Die Grundform ist also `Presenter:action`: ```latte -homepage +home ``` Wenn wir auf die Aktion des aktuellen Moderators verweisen, können wir seinen Namen weglassen: ```latte -homepage +home ``` Wenn die Aktion `default` lautet, können wir sie weglassen, aber der Doppelpunkt muss bleiben: ```latte -homepage +home ``` Links können auch auf andere [Module |modules] verweisen. Hier unterscheidet man zwischen relativen und absoluten Links zu den Untermodulen. Das Prinzip ist analog zu Plattenpfaden, nur dass anstelle von Schrägstrichen Doppelpunkte stehen. Nehmen wir an, dass der eigentliche Präsentator Teil des Moduls `Front` ist, dann schreiben wir: @@ -119,7 +119,7 @@ Ein Sonderfall ist die [Verknüpfung mit sich selbst |#Links to Current Page]. H Wir können auf einen bestimmten Teil der HTML-Seite über ein so genanntes Fragment nach dem Rautezeichen `#` verlinken: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Absolute Pfade .[#toc-absolute-paths] Die von `link()` oder `n:href` erzeugten Links sind immer absolute Pfade (d. h. sie beginnen mit `/`), nicht aber absolute URLs mit Protokoll und Domäne wie `https://domain`. -Um eine absolute URL zu erzeugen, fügen Sie zwei Schrägstriche am Anfang hinzu (z. B. `n:href="//Homepage:"`). Sie können den Präsentator auch so einstellen, dass er nur absolute Links erzeugt, indem Sie `$this->absoluteUrls = true` einstellen. +Um eine absolute URL zu erzeugen, fügen Sie zwei Schrägstriche am Anfang hinzu (z. B. `n:href="//Home:"`). Sie können den Präsentator auch so einstellen, dass er nur absolute Links erzeugt, indem Sie `$this->absoluteUrls = true` einstellen. Link zur aktuellen Seite .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Da [Komponenten |components] separate, wiederverwendbare Einheiten sind, die kei Wenn wir auf Präsentatoren in der Komponentenvorlage verlinken wollen, verwenden wir das Tag `{plink}`: ```latte -homepage +home ``` oder im Code ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/de/how-it-works.texy b/application/de/how-it-works.texy index c99da75b2b..ae27a40ede 100644 --- a/application/de/how-it-works.texy +++ b/application/de/how-it-works.texy @@ -23,10 +23,10 @@ Die Verzeichnisstruktur sieht in etwa so aus: web-project/ ├── app/ ← Verzeichnis mit Anwendung │ ├── Presenters/ ← Presenter-Klassen -│ │ ├── HomepagePresenter.php ← Homepage presenterklasse +│ │ ├── HomePresenter.php ← Home presenterklasse │ │ └── templates/ ← Vorlagenverzeichnis │ │ ├── @layout.latte ← Vorlage für gemeinsames Layout -│ │ └── Homepage/ ← Vorlagen für Homepage-presenter +│ │ └── Home/ ← Vorlagen für Home-presenter │ │ └── default.latte ← Vorlage für Aktion `default` │ ├── Router/ ← Konfiguration von URL-Adressen │ └── Bootstrap.php ← bootende Klasse Bootstrap @@ -134,10 +134,10 @@ Um sicherzugehen, versuchen wir, den gesamten Prozess mit einer etwas anderen UR 1) Die URL lautet dann `https://example.com` 2) wir booten die Anwendung, erstellen einen Container und starten `Application::run()` -3) der Router dekodiert die URL als ein Paar `Homepage:default` -4) ein `HomepagePresenter` Objekt wird erstellt +3) der Router dekodiert die URL als ein Paar `Home:default` +4) ein `HomePresenter` Objekt wird erstellt 5) die Methode `renderDefault()` wird aufgerufen (falls vorhanden) -6) eine Vorlage `templates/Homepage/default.latte` mit einem Layout `templates/@layout.latte` wird gerendert +6) eine Vorlage `templates/Home/default.latte` mit einem Layout `templates/@layout.latte` wird gerendert Vielleicht sind Sie jetzt auf eine Menge neuer Konzepte gestoßen, aber wir glauben, dass sie sinnvoll sind. Das Erstellen von Anwendungen in Nette ist ein Kinderspiel. diff --git a/application/de/modules.texy b/application/de/modules.texy index 6481f57995..fcdf4331c4 100644 --- a/application/de/modules.texy +++ b/application/de/modules.texy @@ -104,7 +104,7 @@ Abbildung .[#toc-mapping] Legt die Regeln fest, nach denen der Klassenname aus dem Namen des Präsentators abgeleitet wird. Wir schreiben sie in die [Konfiguration |configuration] unter dem Schlüssel `application › mapping`. -Beginnen wir mit einem Beispiel, das keine Module verwendet. Wir wollen nur, dass die Presenter-Klassen den Namespace `App\Presenters` haben. Das bedeutet, dass ein Presenter wie `Homepage` auf die Klasse `App\Presenters\HomepagePresenter` abgebildet werden soll. Dies kann durch die folgende Konfiguration erreicht werden: +Beginnen wir mit einem Beispiel, das keine Module verwendet. Wir wollen nur, dass die Presenter-Klassen den Namespace `App\Presenters` haben. Das bedeutet, dass ein Presenter wie `Home` auf die Klasse `App\Presenters\HomePresenter` abgebildet werden soll. Dies kann durch die folgende Konfiguration erreicht werden: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Der Referent `Front:Homepage` wird der Klasse `App\Modules\Front\Presenters\HomepagePresenter` zugeordnet und der Referent `Admin:Dashboard` der Klasse `App\Modules\Admin\Presenters\DashboardPresenter`. +Der Referent `Front:Home` wird der Klasse `App\Modules\Front\Presenters\HomePresenter` zugeordnet und der Referent `Admin:Dashboard` der Klasse `App\Modules\Admin\Presenters\DashboardPresenter`. Es ist praktischer, eine allgemeine (Stern-)Regel zu erstellen, um die ersten beiden zu ersetzen. Das zusätzliche Sternchen wird der Klassenmaske nur für dieses Modul hinzugefügt: diff --git a/application/de/routing.texy b/application/de/routing.texy index 438c8ff3c0..1cd5fcc40d 100644 --- a/application/de/routing.texy +++ b/application/de/routing.texy @@ -93,12 +93,12 @@ Die Route wird nun die URL `https://any-domain.com/chronicle/` mit dem Parameter Natürlich kann auch der Name des Präsentators und der Aktion ein Parameter sein. Zum Beispiel: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Diese Route nimmt z.B. eine URL in der Form `/article/edit` bzw. `/catalog/list` entgegen und übersetzt sie in Präsentatoren und Aktionen `Article:edit` bzw. `Catalog:list`. -Außerdem werden den Parametern `presenter` und `action` die Standardwerte`Homepage` und `default` zugewiesen, so dass sie optional sind. So akzeptiert die Route auch eine URL `/article` und übersetzt sie als `Article:default`. Oder umgekehrt, ein Link auf `Product:default` erzeugt einen Pfad `/product`, ein Link auf den Standardwert `Homepage:default` erzeugt einen Pfad `/`. +Außerdem werden den Parametern `presenter` und `action` die Standardwerte`Home` und `default` zugewiesen, so dass sie optional sind. So akzeptiert die Route auch eine URL `/article` und übersetzt sie als `Article:default`. Oder umgekehrt, ein Link auf `Product:default` erzeugt einen Pfad `/product`, ein Link auf den Standardwert `Home:default` erzeugt einen Pfad `/`. Die Maske kann nicht nur den relativen Pfad auf der Grundlage des Stammverzeichnisses der Website beschreiben, sondern auch den absoluten Pfad, wenn er mit einem Schrägstrich beginnt, oder sogar die gesamte absolute URL, wenn sie mit zwei Schrägstrichen beginnt: @@ -160,7 +160,7 @@ Sequenzen können frei verschachtelt und kombiniert werden: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Akzeptierte URLs: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Optionale Parameter (d.h. Parameter mit Standardwerten) ohne eckige Klammern verhalten sich so, als wären sie so umbrochen: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // gleichbedeutend mit: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Um zu ändern, wie der Schrägstrich ganz rechts erzeugt wird, d. h. statt `/homepage/` ein `/homepage` zu erhalten, passen Sie die Route folgendermaßen an: +Um zu ändern, wie der Schrägstrich ganz rechts erzeugt wird, d. h. statt `/home/` ein `/home` zu erhalten, passen Sie die Route folgendermaßen an: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Der zweite Parameter der Route, den wir oft im Format `Presenter:action` schreib ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filter und Übersetzungen .[#toc-filters-and-translations] Es ist eine gute Praxis, den Quellcode auf Englisch zu schreiben, aber was ist, wenn die URL Ihrer Website in eine andere Sprache übersetzt werden soll? Einfache Routen wie z.B.: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` erzeugen englische URLs, wie `/product/123` oder `/cart`. Wenn wir Präsentatoren und Aktionen in der URL ins Deutsche übersetzt haben wollen (z. B. `/produkt/123` oder `/einkaufswagen`), können wir ein Übersetzungswörterbuch verwenden. Um es hinzuzufügen, benötigen wir bereits eine "gesprächigere" Variante des zweiten Parameters: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // String in URL => Präsentator 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Neben Filtern für bestimmte Parameter können Sie auch allgemeine Filter defini use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Der Parameter des `SimpleRouter` -Konstruktors ist ein Standard-Präsentator und eine Aktion, d.h. eine Aktion, die ausgeführt wird, wenn wir z.B. `http://example.com/` ohne zusätzliche Parameter öffnen. ```php -// Standardmäßig wird der Präsentator 'Homepage' und die Aktion 'default' verwendet -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// Standardmäßig wird der Präsentator 'Home' und die Aktion 'default' verwendet +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Wir empfehlen, SimpleRouter direkt in der [Konfiguration |dependency-injection:services] zu definieren: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Bei der Verarbeitung der Anfrage müssen wir zumindest den Präsentator und die ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/de/templates.texy b/application/de/templates.texy index 65639b369c..10b1ae1db5 100644 --- a/application/de/templates.texy +++ b/application/de/templates.texy @@ -148,7 +148,7 @@ In der Vorlage erstellen wir Links zu anderen Präsentatoren und Aktionen wie fo Das Attribut `n:href` ist sehr praktisch für HTML-Tags ``. Wenn wir den Link an anderer Stelle, zum Beispiel im Text, ausgeben wollen, verwenden wir `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Weitere Informationen finden Sie unter [Links erstellen |Creating Links]. diff --git a/application/el/ajax.texy b/application/el/ajax.texy index aa08c15d07..7e9e5c2d76 100644 --- a/application/el/ajax.texy +++ b/application/el/ajax.texy @@ -149,7 +149,7 @@ $this->isControlInvalid('footer'); // -> true Στο παραπάνω παράδειγμα πρέπει να βεβαιωθείτε ότι για μια αίτηση AJAX θα προστεθεί μόνο ένα στοιχείο στον πίνακα `$list`, επομένως ο βρόχος `foreach` θα εκτυπώσει μόνο ένα δυναμικό απόσπασμα. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/el/creating-links.texy b/application/el/creating-links.texy index 5e914b2863..e36d982225 100644 --- a/application/el/creating-links.texy +++ b/application/el/creating-links.texy @@ -52,7 +52,7 @@ Το χαρακτηριστικό `n:href` είναι πολύ χρήσιμο για τις ετικέτες HTML ``. Αν θέλουμε να εκτυπώσουμε τον σύνδεσμο αλλού, για παράδειγμα στο κείμενο, χρησιμοποιούμε το `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Η βασική μορφή είναι επομένως `Presenter:action`: ```latte -homepage +home ``` Αν συνδεθούμε με τη δράση του τρέχοντος παρουσιαστή, μπορούμε να παραλείψουμε το όνομά του: ```latte -homepage +home ``` Αν η ενέργεια είναι `default`, μπορούμε να την παραλείψουμε, αλλά η άνω και κάτω τελεία πρέπει να παραμείνει: ```latte -homepage +home ``` Οι σύνδεσμοι μπορούν επίσης να παραπέμπουν σε άλλες [ενότητες |modules]. Εδώ, οι σύνδεσμοι διακρίνονται σε σχετικούς με τις υποενότητες ή απόλυτους. Η αρχή είναι ανάλογη με τις διαδρομές δίσκου, μόνο που αντί για κάθετους υπάρχουν άνω και κάτω τελεία. Ας υποθέσουμε ότι ο πραγματικός παρουσιαστής είναι μέρος της ενότητας `Front`, τότε θα γράψουμε: @@ -119,7 +119,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Μπορούμε να συνδεθούμε σε ένα συγκεκριμένο τμήμα της σελίδας HTML μέσω ενός λεγόμενου αποσπάσματος μετά το σύμβολο κατακερματισμού `#`: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Οι σύνδεσμοι που δημιουργούνται από το `link()` ή το `n:href` είναι πάντα απόλυτες διαδρομές (δηλ. ξεκινούν με `/`), αλλά όχι απόλυτες διευθύνσεις URL με πρωτόκολλο και τομέα όπως `https://domain`. -Για να δημιουργήσετε μια απόλυτη διεύθυνση URL, προσθέστε δύο κάθετους στην αρχή (π.χ. `n:href="//Homepage:"`). Ή μπορείτε να αλλάξετε τον παρουσιαστή ώστε να παράγει μόνο απόλυτους συνδέσμους, ρυθμίζοντας το `$this->absoluteUrls = true`. +Για να δημιουργήσετε μια απόλυτη διεύθυνση URL, προσθέστε δύο κάθετους στην αρχή (π.χ. `n:href="//Home:"`). Ή μπορείτε να αλλάξετε τον παρουσιαστή ώστε να παράγει μόνο απόλυτους συνδέσμους, ρυθμίζοντας το `$this->absoluteUrls = true`. Σύνδεσμος προς την τρέχουσα σελίδα .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Αν θέλουμε να συνδέσουμε με παρουσιαστές στο πρότυπο συστατικού, χρησιμοποιούμε την ετικέτα `{plink}`: ```latte -homepage +home ``` ή στον κώδικα ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/el/how-it-works.texy b/application/el/how-it-works.texy index 138f0006ee..b5b1f766dd 100644 --- a/application/el/how-it-works.texy +++ b/application/el/how-it-works.texy @@ -23,10 +23,10 @@ web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ class ProductPresenter extends Nette\Application\UI\Presenter 1) η διεύθυνση URL θα είναι `https://example.com` 2) εκκινούμε την εφαρμογή, δημιουργούμε έναν περιέκτη και εκτελούμε `Application::run()` -3) ο δρομολογητής αποκωδικοποιεί τη διεύθυνση URL ως ζεύγος `Homepage:default` -4) δημιουργείται ένα αντικείμενο `HomepagePresenter` +3) ο δρομολογητής αποκωδικοποιεί τη διεύθυνση URL ως ζεύγος `Home:default` +4) δημιουργείται ένα αντικείμενο `HomePresenter` 5) καλείται η μέθοδος `renderDefault()` (αν υπάρχει) -6) αποδίδεται ένα πρότυπο `templates/Homepage/default.latte` με διάταξη `templates/@layout.latte` +6) αποδίδεται ένα πρότυπο `templates/Home/default.latte` με διάταξη `templates/@layout.latte` Μπορεί να έχετε συναντήσει πολλές νέες έννοιες τώρα, αλλά πιστεύουμε ότι βγάζουν νόημα. Η δημιουργία εφαρμογών στη Nette είναι πανεύκολη. diff --git a/application/el/modules.texy b/application/el/modules.texy index 56dc5ee920..b2bb1dffd0 100644 --- a/application/el/modules.texy +++ b/application/el/modules.texy @@ -104,7 +104,7 @@ class DashboardPresenter extends Nette\Application\UI\Presenter Καθορίζει τους κανόνες με τους οποίους το όνομα της κλάσης προκύπτει από το όνομα του παρουσιαστή. Τους γράφουμε στη [διαμόρφωση |configuration] κάτω από το κλειδί `application › mapping`. -Ας ξεκινήσουμε με ένα δείγμα που δεν χρησιμοποιεί ενότητες. Θα θέλουμε απλώς οι κλάσεις presenter να έχουν το namespace `App\Presenters`. Αυτό σημαίνει ότι ένας παρουσιαστής όπως το `Homepage` θα πρέπει να αντιστοιχίζεται στην κλάση `App\Presenters\HomepagePresenter`. Αυτό μπορεί να επιτευχθεί με την ακόλουθη διαμόρφωση: +Ας ξεκινήσουμε με ένα δείγμα που δεν χρησιμοποιεί ενότητες. Θα θέλουμε απλώς οι κλάσεις presenter να έχουν το namespace `App\Presenters`. Αυτό σημαίνει ότι ένας παρουσιαστής όπως το `Home` θα πρέπει να αντιστοιχίζεται στην κλάση `App\Presenters\HomePresenter`. Αυτό μπορεί να επιτευχθεί με την ακόλουθη διαμόρφωση: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Τώρα ο παρουσιαστής `Front:Homepage` αντιστοιχίζεται στην κλάση `App\Modules\Front\Presenters\HomepagePresenter` και ο παρουσιαστής `Admin:Dashboard` στην κλάση `App\Modules\Admin\Presenters\DashboardPresenter`. +Τώρα ο παρουσιαστής `Front:Home` αντιστοιχίζεται στην κλάση `App\Modules\Front\Presenters\HomePresenter` και ο παρουσιαστής `Admin:Dashboard` στην κλάση `App\Modules\Admin\Presenters\DashboardPresenter`. Είναι πιο πρακτικό να δημιουργήσετε έναν γενικό κανόνα (αστέρι) για να αντικαταστήσετε τους δύο πρώτους. Ο επιπλέον αστερίσκος θα προστεθεί στη μάσκα κλάσης μόνο για την ενότητα: diff --git a/application/el/routing.texy b/application/el/routing.texy index 55da217819..19b2ceb7bf 100644 --- a/application/el/routing.texy +++ b/application/el/routing.texy @@ -93,12 +93,12 @@ $router->addRoute('chronicle/', 'History:show'); Φυσικά, το όνομα του παρουσιαστή και της ενέργειας μπορεί επίσης να είναι παράμετρος. Για παράδειγμα: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Αυτή η διαδρομή δέχεται, για παράδειγμα, μια διεύθυνση URL της μορφής `/article/edit` ή `/catalog/list` και τις μεταφράζει σε παρουσιαστές και ενέργειες `Article:edit` ή `Catalog:list`. -Δίνει επίσης στις παραμέτρους `presenter` και `action` προεπιλεγμένες τιμές`Homepage` και `default` και επομένως είναι προαιρετικές. Έτσι, η διαδρομή δέχεται επίσης ένα URL `/article` και το μεταφράζει ως `Article:default`. Ή αντίστροφα, ένας σύνδεσμος προς το `Product:default` δημιουργεί μια διαδρομή `/product`, ένας σύνδεσμος προς την προεπιλεγμένη `Homepage:default` δημιουργεί μια διαδρομή `/`. +Δίνει επίσης στις παραμέτρους `presenter` και `action` προεπιλεγμένες τιμές`Home` και `default` και επομένως είναι προαιρετικές. Έτσι, η διαδρομή δέχεται επίσης ένα URL `/article` και το μεταφράζει ως `Article:default`. Ή αντίστροφα, ένας σύνδεσμος προς το `Product:default` δημιουργεί μια διαδρομή `/product`, ένας σύνδεσμος προς την προεπιλεγμένη `Home:default` δημιουργεί μια διαδρομή `/`. Η μάσκα μπορεί να περιγράφει όχι μόνο τη σχετική διαδρομή με βάση τη ρίζα του ιστότοπου, αλλά και την απόλυτη διαδρομή όταν αρχίζει με μια κάθετο, ή ακόμη και ολόκληρη την απόλυτη διεύθυνση URL όταν αρχίζει με δύο κάθετους: @@ -160,7 +160,7 @@ $router->addRoute('//[.]example.com//', /* ... */); ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Αποδεκτές διευθύνσεις URL: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Οι προαιρετικές παράμετροι (δηλαδή οι παράμετροι που έχουν προεπιλεγμένη τιμή) χωρίς τετράγωνες αγκύλες συμπεριφέρονται σαν να είναι τυλιγμένες έτσι: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // ισούται με: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Για να αλλάξετε τον τρόπο με τον οποίο παράγεται η δεξιότερη κάθετος, δηλαδή αντί για `/homepage/` να πάρετε ένα `/homepage`, προσαρμόστε τη διαδρομή με αυτόν τον τρόπο: +Για να αλλάξετε τον τρόπο με τον οποίο παράγεται η δεξιότερη κάθετος, δηλαδή αντί για `/home/` να πάρετε ένα `/home`, προσαρμόστε τη διαδρομή με αυτόν τον τρόπο: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ $router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ $router->addRoute('/[/]', [ Είναι μια καλή πρακτική να γράφετε τον πηγαίο κώδικα στα αγγλικά, αλλά τι γίνεται αν θέλετε ο ιστότοπός σας να έχει μεταφρασμένο URL σε διαφορετική γλώσσα; Απλές διαδρομές, όπως: "Η γλώσσα που θα χρησιμοποιηθεί για να μεταφραστεί σε άλλη γλώσσα": ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` θα δημιουργήσουν αγγλικές διευθύνσεις URL, όπως `/product/123` ή `/cart`. Αν θέλουμε να έχουμε παρουσιαστές και ενέργειες στη διεύθυνση URL μεταφρασμένες στα γερμανικά (π.χ. `/produkt/123` ή `/einkaufswagen`), μπορούμε να χρησιμοποιήσουμε ένα μεταφραστικό λεξικό. Για να το προσθέσουμε, χρειαζόμαστε ήδη μια "πιο ομιλητική" παραλλαγή της δεύτερης παραμέτρου: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // συμβολοσειρά στη διεύθυνση URL => παρουσιαστής 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ $router->addRoute('//', [ use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Η παράμετρος του κατασκευαστή `SimpleRouter` είναι ένας προεπιλεγμένος παρουσιαστής & ενέργεια, δηλαδή ενέργεια που θα εκτελεστεί αν ανοίξουμε π.χ. το `http://example.com/` χωρίς πρόσθετες παραμέτρους. ```php -// προεπιλογή παρουσιαστή 'Homepage' και δράσης 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// προεπιλογή παρουσιαστή 'Home' και δράσης 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Συνιστούμε τον ορισμό του SimpleRouter απευθείας στη [διαμόρφωση |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ class MyRouter implements Nette\Routing\Router ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/el/templates.texy b/application/el/templates.texy index 364d15388a..c4ee865e55 100644 --- a/application/el/templates.texy +++ b/application/el/templates.texy @@ -148,7 +148,7 @@ Default Variables .[#toc-default-variables] Το χαρακτηριστικό `n:href` είναι πολύ βολικό για τις ετικέτες HTML ``. Αν θέλουμε να εκτυπώσουμε τον σύνδεσμο αλλού, για παράδειγμα στο κείμενο, χρησιμοποιούμε το `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Για περισσότερες πληροφορίες, ανατρέξτε στην ενότητα [Δημιουργία συνδέσμων |Creating Links]. diff --git a/application/en/ajax.texy b/application/en/ajax.texy index 5e26e4ced2..b5d635a10c 100644 --- a/application/en/ajax.texy +++ b/application/en/ajax.texy @@ -149,7 +149,7 @@ You can't redraw a dynamic snippet directly (redrawing of `item-1` has no effect In the example above you have to make sure that for an AJAX request only one item will be added to the `$list` array, therefore the `foreach` loop will print just one dynamic snippet. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/en/creating-links.texy b/application/en/creating-links.texy index 3415287796..0fb783c609 100644 --- a/application/en/creating-links.texy +++ b/application/en/creating-links.texy @@ -52,7 +52,7 @@ The so-called [persistent parameters|presenters#persistent parameters] are also Attribute `n:href` is very handy for HTML tags ``. If we want to print the link elsewhere, for example in the text, we use `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ The format is supported by all Latte tags and all presenter methods that work wi The basic form is therefore `Presenter:action`: ```latte -homepage +home ``` If we link to the action of the current presenter, we can omit its name: ```latte -homepage +home ``` If the action is `default`, we can omit it, but the colon must remain: ```latte -homepage +home ``` Links may also point to other [modules]. Here, the links are distinguished into relative to the submodules, or absolute. The principle is analogous to disk paths, only instead of slashes there are colons. Let's assume that the actual presenter is part of module `Front`, then we will write: @@ -119,7 +119,7 @@ A special case is [linking to itself|#Links to Current Page]. Here we'll write ` We can link to a certain part of the HTML page via a so-called fragment after the `#` hash symbol: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Absolute Paths Links generated by `link()` or `n:href` are always absolute paths (i.e., they start with `/`), but not absolute URLs with a protocol and domain like `https://domain`. -To generate an absolute URL, add two slashes to the beginning (e.g., `n:href="//Homepage:"`). Or you can switch the presenter to generate only absolute links by setting `$this->absoluteUrls = true`. +To generate an absolute URL, add two slashes to the beginning (e.g., `n:href="//Home:"`). Or you can switch the presenter to generate only absolute links by setting `$this->absoluteUrls = true`. Link to Current Page @@ -213,13 +213,13 @@ Because [components] are separate reusable units that should have no relations t If we want to link to presenters in the component template, we use the tag `{plink}`: ```latte -homepage +home ``` or in the code ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/en/how-it-works.texy b/application/en/how-it-works.texy index 2feed87b13..569d48065f 100644 --- a/application/en/how-it-works.texy +++ b/application/en/how-it-works.texy @@ -23,10 +23,10 @@ The directory structure looks something like this: web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ Just to be sure, let's try to recap the whole process with a slightly different 1) the URL will be `https://example.com` 2) we boot the application, create a container and run `Application::run()` -3) the router decodes the URL as a pair `Homepage:default` -4) an `HomepagePresenter` object is created +3) the router decodes the URL as a pair `Home:default` +4) an `HomePresenter` object is created 5) method `renderDefault()` is called (if exists) -6) a template `templates/Homepage/default.latte` with a layout `templates/@layout.latte` is rendered +6) a template `templates/Home/default.latte` with a layout `templates/@layout.latte` is rendered You may have come across a lot of new concepts now, but we believe they make sense. Creating applications in Nette is a breeze. diff --git a/application/en/modules.texy b/application/en/modules.texy index 3cddb2754f..6312143fdd 100644 --- a/application/en/modules.texy +++ b/application/en/modules.texy @@ -104,7 +104,7 @@ Mapping Defines the rules by which the class name is derived from the presenter name. We write them in [configuration] under the `application › mapping` key. -Let's start with a sample that doesn't use modules. We'll just want the presenter classes to have the `App\Presenters` namespace. That means that a presenter such as `Homepage` should map to the `App\Presenters\HomepagePresenter` class. This can be achieved by the following configuration: +Let's start with a sample that doesn't use modules. We'll just want the presenter classes to have the `App\Presenters` namespace. That means that a presenter such as `Home` should map to the `App\Presenters\HomePresenter` class. This can be achieved by the following configuration: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Now presenter `Front:Homepage` maps to class `App\Modules\Front\Presenters\HomepagePresenter` and presenter `Admin:Dashboard` to class `App\Modules\Admin\Presenters\DashboardPresenter`. +Now presenter `Front:Home` maps to class `App\Modules\Front\Presenters\HomePresenter` and presenter `Admin:Dashboard` to class `App\Modules\Admin\Presenters\DashboardPresenter`. It is more practical to create a general (star) rule to replace the first two. The extra asterisk will be added to the class mask just for the module: diff --git a/application/en/routing.texy b/application/en/routing.texy index 55df69e79f..bb30283d14 100644 --- a/application/en/routing.texy +++ b/application/en/routing.texy @@ -93,12 +93,12 @@ The route will now accept the URL `https://any-domain.com/chronicle/`, which wil Of course, the name of the presenter and the action can also be a parameter. For example: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` This route accepts, for example, a URL in the form `/article/edit` resp. `/catalog/list` and translates them to presenters and actions `Article:edit` resp. `Catalog:list`. -It also gives to parameters `presenter` and `action` default values ​​`Homepage` and `default` and therefore they are optional. So the route also accepts a URL `/article` and translates it as `Article:default`. Or vice versa, a link to `Product:default` generates a path `/product`, a link to the default `Homepage:default` generates a path `/`. +It also gives to parameters `presenter` and `action` default values ​​`Home` and `default` and therefore they are optional. So the route also accepts a URL `/article` and translates it as `Article:default`. Or vice versa, a link to `Product:default` generates a path `/product`, a link to the default `Home:default` generates a path `/`. The mask can describe not only the relative path based on the site root, but also the absolute path when it begins with a slash, or even the entire absolute URL when it begins with two slashes: @@ -160,7 +160,7 @@ Sequences may be freely nested and combined: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Accepted URLs: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Optional parameters (ie. parameters having default value) without square brackets do behave as if wrapped like this: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // equals to: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -To change how the rightmost slash is generated, i.e. instead of `/homepage/` get a `/homepage`, adjust the route this way: +To change how the rightmost slash is generated, i.e. instead of `/home/` get a `/home`, adjust the route this way: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ The second parameter of the route, which we often write in the format `Presenter ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filters and Translations It's a good practice to write source code in English, but what if you need your website to have translated URL to different language? Simple routes such as: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` will generate English URLs, such as `/product/123` or `/cart`. If we want to have presenters and actions in the URL translated to Deutsch (e.g. `/produkt/123` or `/einkaufswagen`), we can use a translation dictionary. To add it, we already need a "more talkative" variant of the second parameter: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // string in URL => presenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Besides filters for specific parameters, you can also define general filters tha use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 The parameter of the `SimpleRouter` constructor is a default presenter & action, ie. action to be executed if we open e.g. `http://example.com/` without additional parameters. ```php -// defaults to presenter 'Homepage' and action 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// defaults to presenter 'Home' and action 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` We recommend defining SimpleRouter directly in [configuration |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ When processing the request, we must return at least the presenter and the actio ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/en/templates.texy b/application/en/templates.texy index 89a8e2cf3c..9c743b280b 100644 --- a/application/en/templates.texy +++ b/application/en/templates.texy @@ -148,7 +148,7 @@ In template we create links to other presenters & actions as follows: Attribute `n:href` is very handy for HTML tags ``. If we want to print the link elsewhere, for example in the text, we use `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` For more information, see [Creating Links]. diff --git a/application/es/ajax.texy b/application/es/ajax.texy index 17e7543771..a81eb64441 100644 --- a/application/es/ajax.texy +++ b/application/es/ajax.texy @@ -149,7 +149,7 @@ No puedes redibujar un fragmento dinámico directamente (redibujar `item-1` no t En el ejemplo anterior tiene que asegurarse de que para una petición AJAX sólo se añadirá un elemento a la matriz `$list`, por lo que el bucle `foreach` sólo imprimirá un fragmento dinámico. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/es/creating-links.texy b/application/es/creating-links.texy index 1b8304bb6f..cd4645f3a5 100644 --- a/application/es/creating-links.texy +++ b/application/es/creating-links.texy @@ -52,7 +52,7 @@ Los llamados [parámetros persistentes |presenters#persistent parameters] tambi El atributo `n:href` es muy útil para las etiquetas HTML ``. Si queremos imprimir el enlace en otro lugar, por ejemplo en el texto, utilizamos `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ El formato es soportado por todas las etiquetas Latte y todos los métodos de pr Por lo tanto, la forma básica es `Presenter:action`: ```latte -homepage +home ``` Si enlazamos con la acción del presentador actual, podemos omitir su nombre: ```latte -homepage +home ``` Si la acción es `default`, podemos omitirlo, pero los dos puntos deben permanecer: ```latte -homepage +home ``` Los enlaces también pueden apuntar a otros [módulos |modules]. Aquí, los enlaces se distinguen en relativos a los submódulos, o absolutos. El principio es análogo a las rutas de disco, sólo que en lugar de barras inclinadas hay dos puntos. Supongamos que el presentador real forma parte del módulo `Front`, entonces escribiremos: @@ -119,7 +119,7 @@ Un caso especial es el [enlace a sí mismo |#Links to Current Page]. Aquí escri Podemos enlazar a una parte determinada de la página HTML mediante un fragmento llamado `#` después del símbolo de almohadilla `#`: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Rutas absolutas .[#toc-absolute-paths] Los enlaces generados por `link()` o `n:href` son siempre rutas absolutas (es decir, empiezan por `/`), pero no URLs absolutas con protocolo y dominio como `https://domain`. -Para generar una URL absoluta, añada dos barras al principio (por ejemplo, `n:href="//Homepage:"`). También puede hacer que el presentador genere sólo enlaces absolutos configurando `$this->absoluteUrls = true`. +Para generar una URL absoluta, añada dos barras al principio (por ejemplo, `n:href="//Home:"`). También puede hacer que el presentador genere sólo enlaces absolutos configurando `$this->absoluteUrls = true`. Enlace a la página actual .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Dado que los [componentes |components] son unidades reutilizables independientes Si queremos enlazar con presentadores en la plantilla de componentes, utilizaremos la etiqueta `{plink}`: ```latte -homepage +home ``` o en el código ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/es/how-it-works.texy b/application/es/how-it-works.texy index f414f77972..41276637aa 100644 --- a/application/es/how-it-works.texy +++ b/application/es/how-it-works.texy @@ -23,10 +23,10 @@ La estructura de directorios se parece a esto web-project/ ├── app/ ← directorio con la aplicación │ ├── Presenters/ ← clases para presentadores -│ │ ├── HomepagePresenter.php ← Homepage de inicio de la clase de presentador +│ │ ├── HomePresenter.php ← Home de inicio de la clase de presentador │ │ └── templates/ ← directorio de plantillas │ │ ├── @layout.latte ← plantilla de diseño compartida -│ │ └── Homepage/ ← plantillas para Homepage presentador de inicio +│ │ └── Home/ ← plantillas para Home presentador de inicio │ │ └── default.latte ← plantilla para la acción `default` │ ├── Router/ ← configuración de direcciones URL │ └── Bootstrap.php ← clase de arranque Bootstrap @@ -134,10 +134,10 @@ Sólo para estar seguros, intentemos recapitular todo el proceso con una URL lig 1) la URL será `https://example.com` 2) arrancamos la aplicación, creamos un contenedor y ejecutamos `Application::run()` -3) el router decodifica la URL como un par `Homepage:default` -4) se crea un objeto `HomepagePresenter` +3) el router decodifica la URL como un par `Home:default` +4) se crea un objeto `HomePresenter` 5) se llama al método `renderDefault()` (si existe) -6) se renderiza una plantilla `templates/Homepage/default.latte` con un diseño `templates/@layout.latte` +6) se renderiza una plantilla `templates/Home/default.latte` con un diseño `templates/@layout.latte` Puede que ahora te hayas encontrado con un montón de conceptos nuevos, pero creemos que tienen sentido. Crear aplicaciones en Nette es pan comido. diff --git a/application/es/modules.texy b/application/es/modules.texy index 3b04ffbe05..e183346e5f 100644 --- a/application/es/modules.texy +++ b/application/es/modules.texy @@ -104,7 +104,7 @@ Mapeo .[#toc-mapping] Define las reglas por las que el nombre de la clase se deriva del nombre del presentador. Las escribimos en [configuración |configuration] bajo la clave `application › mapping`. -Empecemos con un ejemplo que no utiliza módulos. Sólo querremos que las clases del presentador tengan el espacio de nombres `App\Presenters`. Eso significa que un presentador como `Homepage` debe mapearse a la clase `App\Presenters\HomepagePresenter`. Esto se puede lograr con la siguiente configuración: +Empecemos con un ejemplo que no utiliza módulos. Sólo querremos que las clases del presentador tengan el espacio de nombres `App\Presenters`. Eso significa que un presentador como `Home` debe mapearse a la clase `App\Presenters\HomePresenter`. Esto se puede lograr con la siguiente configuración: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Ahora el presentador `Front:Homepage` se asigna a la clase `App\Modules\Front\Presenters\HomepagePresenter` y el presentador `Admin:Dashboard` a la clase `App\Modules\Admin\Presenters\DashboardPresenter`. +Ahora el presentador `Front:Home` se asigna a la clase `App\Modules\Front\Presenters\HomePresenter` y el presentador `Admin:Dashboard` a la clase `App\Modules\Admin\Presenters\DashboardPresenter`. Es más práctico crear una regla general (estrella) para sustituir a las dos primeras. El asterisco adicional se añadirá a la máscara de clase sólo para el módulo: diff --git a/application/es/routing.texy b/application/es/routing.texy index 058cfd31b8..2e886aa907 100644 --- a/application/es/routing.texy +++ b/application/es/routing.texy @@ -93,12 +93,12 @@ La ruta aceptará ahora la URL `https://any-domain.com/chronicle/` con el parám Por supuesto, el nombre del presentador y la acción también pueden ser un parámetro. Por ejemplo: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Esta ruta acepta, por ejemplo, una URL de la forma `/article/edit` resp. `/catalog/list` y las traduce a presentadores y acciones `Article:edit` resp. `Catalog:list`. -También da a los parámetros `presenter` y `action` valores por defecto`Homepage` y `default` y, por tanto, son opcionales. Así, la ruta también acepta una URL `/article` y la traduce como `Article:default`. O viceversa, un enlace a `Product:default` genera una ruta `/product`, un enlace al valor por defecto `Homepage:default` genera una ruta `/`. +También da a los parámetros `presenter` y `action` valores por defecto`Home` y `default` y, por tanto, son opcionales. Así, la ruta también acepta una URL `/article` y la traduce como `Article:default`. O viceversa, un enlace a `Product:default` genera una ruta `/product`, un enlace al valor por defecto `Home:default` genera una ruta `/`. La máscara puede describir no sólo la ruta relativa basada en la raíz del sitio, sino también la ruta absoluta cuando comienza con una barra, o incluso toda la URL absoluta cuando comienza con dos barras: @@ -160,7 +160,7 @@ Las secuencias pueden anidarse y combinarse libremente: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // URL aceptadas: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Los parámetros opcionales (es decir, los parámetros que tienen un valor por defecto) sin corchetes se comportan como si estuvieran envueltos de esta manera: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // igual a: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Para cambiar cómo se genera la barra más a la derecha, es decir, en lugar de `/homepage/` obtener un `/homepage`, ajustar la ruta de esta manera: +Para cambiar cómo se genera la barra más a la derecha, es decir, en lugar de `/home/` obtener un `/home`, ajustar la ruta de esta manera: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ El segundo parámetro de la ruta, que a menudo escribimos en el formato `Present ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtros y traducciones .[#toc-filters-and-translations] Es una buena práctica escribir el código fuente en inglés, pero ¿qué pasa si necesitas que tu sitio web tenga la URL traducida a otro idioma? Rutas simples como: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` generarán URL en inglés, como `/product/123` o `/cart`. Si queremos que los presentadores y las acciones de la URL se traduzcan al alemán (por ejemplo, `/produkt/123` o `/einkaufswagen`), podemos utilizar un diccionario de traducción. Para añadirlo, ya necesitamos una variante "más locuaz" del segundo parámetro: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // cadena en URL => presentador 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Además de filtros para parámetros específicos, también puede definir filtros use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 El parámetro del constructor `SimpleRouter` es un presentador y una acción por defecto, es decir, la acción que se ejecutará si abrimos, por ejemplo, `http://example.com/` sin parámetros adicionales. ```php -// por defecto el presentador es 'Homepage' y la acción es 'default -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// por defecto el presentador es 'Home' y la acción es 'default +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Recomendamos definir SimpleRouter directamente en la [configuración |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Al procesar la solicitud, debemos devolver al menos el presentador y la acción. ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/es/templates.texy b/application/es/templates.texy index 1448f09a72..bbae32eb8a 100644 --- a/application/es/templates.texy +++ b/application/es/templates.texy @@ -148,7 +148,7 @@ En la plantilla creamos enlaces a otros presentadores y acciones de la siguiente Atributo `n:href` es muy útil para etiquetas HTML ``. Si queremos imprimir el enlace en otro lugar, por ejemplo en el texto, utilizamos `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Para más información, véase [Creación de enlaces |Creating Links]. diff --git a/application/fr/ajax.texy b/application/fr/ajax.texy index 406327ab56..06f9ffb892 100644 --- a/application/fr/ajax.texy +++ b/application/fr/ajax.texy @@ -149,7 +149,7 @@ Vous ne pouvez pas redessiner un extrait dynamique directement (redessiner `item Dans l'exemple ci-dessus, vous devez vous assurer que, pour une requête AJAX, un seul élément sera ajouté au tableau `$list`. Par conséquent, la boucle `foreach` n'imprimera qu'un seul extrait dynamique. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/fr/creating-links.texy b/application/fr/creating-links.texy index 9194ce7f9d..c6786ac362 100644 --- a/application/fr/creating-links.texy +++ b/application/fr/creating-links.texy @@ -52,7 +52,7 @@ Les [paramètres |presenters#persistent parameters] dits [persistants |presenter L'attribut `n:href` est très pratique pour les balises HTML ``. Si nous voulons imprimer le lien ailleurs, par exemple dans le texte, nous utilisons `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ Le format est pris en charge par toutes les balises Latte et toutes les méthode La forme de base est donc `Presenter:action`: ```latte -homepage +home ``` Si nous établissons un lien avec l'action du présentateur actuel, nous pouvons omettre son nom : ```latte -homepage +home ``` Si l'action est `default`, nous pouvons l'omettre, mais les deux points doivent rester : ```latte -homepage +home ``` Les liens peuvent également pointer vers d'autres [modules]. Ici, on distingue les liens relatifs aux sous-modules et les liens absolus. Le principe est analogue à celui des chemins d'accès aux disques, mais les deux-points remplacent les barres obliques. Supposons que le présentateur actuel fasse partie du module `Front`, nous écrirons alors : @@ -119,7 +119,7 @@ Un cas particulier est la [liaison à lui-même |#Links to Current Page]. Ici, n Nous pouvons créer un lien vers une certaine partie de la page HTML par le biais de ce que l'on appelle un fragment après le symbole dièse `#` : ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Chemins absolus .[#toc-absolute-paths] Les liens générés par `link()` ou `n:href` sont toujours des chemins absolus (c'est-à-dire qu'ils commencent par `/`), mais pas des URL absolus avec un protocole et un domaine comme `https://domain`. -Pour générer une URL absolue, ajoutez deux barres obliques au début (par exemple, `n:href="//Homepage:"`). Vous pouvez aussi faire en sorte que le diffuseur ne génère que des liens absolus en définissant `$this->absoluteUrls = true`. +Pour générer une URL absolue, ajoutez deux barres obliques au début (par exemple, `n:href="//Home:"`). Vous pouvez aussi faire en sorte que le diffuseur ne génère que des liens absolus en définissant `$this->absoluteUrls = true`. Lien vers la page actuelle .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Comme les [composants |components] sont des unités distinctes réutilisables qu Si nous voulons créer un lien vers les présentateurs dans le modèle de composant, nous utilisons la balise `{plink}`: ```latte -homepage +home ``` ou dans le code ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/fr/how-it-works.texy b/application/fr/how-it-works.texy index fa3e6e3d48..065eb5aa33 100644 --- a/application/fr/how-it-works.texy +++ b/application/fr/how-it-works.texy @@ -23,10 +23,10 @@ La structure des répertoires ressemble à ceci : web-project/ ├── app/ ← répertoire avec application │ ├── Presenters/ ← classes d'presenter -│ │ ├── HomepagePresenter.php ← Homepage classe des présentateurs +│ │ ├── HomePresenter.php ← Home classe des présentateurs │ │ └── templates/ ← répertoire des modèles │ │ ├── @layout.latte ← modèle de disposition partagée -│ │ └── Homepage/ ← Modèles pour le présentateur de la page d'accueil +│ │ └── Home/ ← Modèles pour le présentateur de la page d'accueil │ │ └── default.latte ← modèle pour l'action `default` │ ├── Router/ ← configuration des adresses URL │ └── Bootstrap.php ← classe de démarrage Bootstrap @@ -134,10 +134,10 @@ Juste pour être sûr, essayons de récapituler l'ensemble du processus avec une 1) l'URL sera `https://example.com` 2) nous démarrons l'application, nous créons un conteneur et nous l'exécutons `Application::run()` -3) le routeur décode l'URL comme une paire `Homepage:default` -4) un objet `HomepagePresenter` est créé +3) le routeur décode l'URL comme une paire `Home:default` +4) un objet `HomePresenter` est créé 5) la méthode `renderDefault()` est appelée (si elle existe) -6) un modèle `templates/Homepage/default.latte` avec une mise en page `templates/@layout.latte` est rendu +6) un modèle `templates/Home/default.latte` avec une mise en page `templates/@layout.latte` est rendu Vous avez peut-être rencontré beaucoup de nouveaux concepts maintenant, mais nous pensons qu'ils ont un sens. Créer des applications dans Nette est un jeu d'enfant. diff --git a/application/fr/modules.texy b/application/fr/modules.texy index 4b774c8fb7..ade58f61b9 100644 --- a/application/fr/modules.texy +++ b/application/fr/modules.texy @@ -104,7 +104,7 @@ Cartographie .[#toc-mapping] Définit les règles par lesquelles le nom de la classe est dérivé du nom du présentateur. On les inscrit dans la [configuration] sous la clé `application › mapping`. -Commençons par un exemple qui n'utilise pas de modules. Nous voulons simplement que les classes du présentateur aient l'espace de nom `App\Presenters`. Cela signifie qu'un présentateur tel que `Homepage` doit correspondre à la classe `App\Presenters\HomepagePresenter`. Ceci peut être réalisé par la configuration suivante : +Commençons par un exemple qui n'utilise pas de modules. Nous voulons simplement que les classes du présentateur aient l'espace de nom `App\Presenters`. Cela signifie qu'un présentateur tel que `Home` doit correspondre à la classe `App\Presenters\HomePresenter`. Ceci peut être réalisé par la configuration suivante : ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Maintenant, le présentateur `Front:Homepage` correspond à la classe `App\Modules\Front\Presenters\HomepagePresenter` et le présentateur `Admin:Dashboard` à la classe `App\Modules\Admin\Presenters\DashboardPresenter`. +Maintenant, le présentateur `Front:Home` correspond à la classe `App\Modules\Front\Presenters\HomePresenter` et le présentateur `Admin:Dashboard` à la classe `App\Modules\Admin\Presenters\DashboardPresenter`. Il est plus pratique de créer une règle générale (étoile) pour remplacer les deux premières. L'astérisque supplémentaire sera ajouté au masque de classe uniquement pour le module : diff --git a/application/fr/routing.texy b/application/fr/routing.texy index f387a22ac2..a2ec2aff40 100644 --- a/application/fr/routing.texy +++ b/application/fr/routing.texy @@ -93,12 +93,12 @@ La route acceptera maintenant l'URL `https://any-domain.com/chronicle/` avec le Bien entendu, le nom du présentateur et de l'action peut également être un paramètre. Par exemple : ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Cette route accepte, par exemple, une URL sous la forme `/article/edit` resp. `/catalog/list` et les traduit en présentateurs et actions `Article:edit` resp. `Catalog:list`. -Elle donne également aux paramètres `presenter` et `action` des valeurs par défaut`Homepage` et `default` et ils sont donc facultatifs. Ainsi, la route accepte également une URL `/article` et la traduit en `Article:default`. Ou vice versa, un lien vers `Product:default` génère un chemin `/product`, un lien vers le défaut `Homepage:default` génère un chemin `/`. +Elle donne également aux paramètres `presenter` et `action` des valeurs par défaut`Home` et `default` et ils sont donc facultatifs. Ainsi, la route accepte également une URL `/article` et la traduit en `Article:default`. Ou vice versa, un lien vers `Product:default` génère un chemin `/product`, un lien vers le défaut `Home:default` génère un chemin `/`. Le masque peut décrire non seulement le chemin relatif basé sur la racine du site, mais aussi le chemin absolu lorsqu'il commence par une barre oblique, ou même l'URL absolue entière lorsqu'elle commence par deux barres obliques : @@ -160,7 +160,7 @@ Les séquences peuvent être librement imbriquées et combinées : ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // URLs acceptées: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Les paramètres optionnels (c'est-à-dire les paramètres ayant une valeur par défaut) sans crochets se comportent comme s'ils étaient enveloppés de cette façon : ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // équivaut à: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Pour modifier la façon dont la barre oblique la plus à droite est générée, c'est-à-dire qu'au lieu de `/homepage/`, vous obtenez `/homepage`, ajustez la route de cette façon : +Pour modifier la façon dont la barre oblique la plus à droite est générée, c'est-à-dire qu'au lieu de `/home/`, vous obtenez `/home`, ajustez la route de cette façon : ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Le deuxième paramètre de la route, que nous écrivons souvent sous le format ` ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtres et traductions .[#toc-filters-and-translations] C'est une bonne pratique d'écrire le code source en anglais, mais que faire si vous avez besoin que l'URL de votre site Web soit traduite dans une autre langue ? Des routes simples telles que : ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` généreront des URL en anglais, comme `/product/123` ou `/cart`. Si nous voulons que les présentateurs et les actions dans l'URL soient traduits en allemand (par exemple `/produkt/123` ou `/einkaufswagen`), nous pouvons utiliser un dictionnaire de traduction. Pour l'ajouter, nous avons déjà besoin d'une variante "plus bavarde" du deuxième paramètre : @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // string dans l'URL => presenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Outre les filtres pour des paramètres spécifiques, vous pouvez également déf use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Le paramètre du constructeur `SimpleRouter` est un présentateur et une action par défaut, c'est-à-dire une action à exécuter si nous ouvrons par exemple `http://example.com/` sans paramètres supplémentaires. ```php -// par défaut, le présentateur est 'Homepage' et l'action 'default'. -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// par défaut, le présentateur est 'Home' et l'action 'default'. +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Nous recommandons de définir SimpleRouter directement dans la [configuration |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Lors du traitement de la demande, nous devons retourner au moins le diffuseur et ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/fr/templates.texy b/application/fr/templates.texy index 5ee5236ba2..83ac0a9d46 100644 --- a/application/fr/templates.texy +++ b/application/fr/templates.texy @@ -148,7 +148,7 @@ Dans le modèle, nous créons des liens vers d'autres présentateurs et actions L'attribut `n:href` est très pratique pour les balises HTML ``. Si nous voulons imprimer le lien ailleurs, par exemple dans le texte, nous utilisons `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Pour plus d'informations, voir [Création de liens |Creating Links]. diff --git a/application/hu/ajax.texy b/application/hu/ajax.texy index 4d50da3143..adab48653b 100644 --- a/application/hu/ajax.texy +++ b/application/hu/ajax.texy @@ -149,7 +149,7 @@ Egy dinamikus snippet közvetlenül nem rajzolható újra (a `item-1` újrarajzo A fenti példában gondoskodnia kell arról, hogy egy AJAX-kérés esetén csak egy elem kerüljön a `$list` tömbhöz, ezért a `foreach` ciklus csak egy dinamikus snippetet fog kiírni. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/hu/creating-links.texy b/application/hu/creating-links.texy index 801a922bbe..8f7c11af58 100644 --- a/application/hu/creating-links.texy +++ b/application/hu/creating-links.texy @@ -52,7 +52,7 @@ Az úgynevezett [tartós paraméterek |presenters#persistent parameters] is auto A `n:href` attribútum nagyon hasznos a HTML címkékhez. ``. Ha a linket máshol, például a szövegben akarjuk kiírni, akkor a `{link}` attribútumot használjuk: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ A formátumot az összes Latte tag és az összes olyan presenter módszer támo Az alapforma tehát `Presenter:action`: ```latte -homepage +home ``` Ha az aktuális bemutató akciójára hivatkozunk, elhagyhatjuk a nevét: ```latte -homepage +home ``` Ha a művelet a `default`, elhagyhatjuk, de a kettőspontnak meg kell maradnia: ```latte -homepage +home ``` A linkek más [modulokra |modules] is mutathatnak. Itt a linkeket megkülönböztetjük az almodulokhoz viszonyított, illetve abszolút linkekre. Az elv analóg a lemezes elérési utakkal, csak a kötőjelek helyett kettőspontok vannak. Tegyük fel, hogy a tényleges bemutató a `Front` modul része, akkor azt írjuk: @@ -119,7 +119,7 @@ Speciális eset a [saját magára való hivatkozás |#Links to Current Page]. It A HTML oldal egy bizonyos részére a `#` hash szimbólum után egy úgynevezett fragmentummal tudunk hivatkozni: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Abszolút elérési utak .[#toc-absolute-paths] A `link()` vagy `n:href` által generált linkek mindig abszolút elérési utak (azaz `/`-vel kezdődnek), de nem abszolút URL-ek protokollal és domainnel, mint például `https://domain`. -Abszolút URL létrehozásához adjon hozzá két kötőjelet az elejéhez (pl. `n:href="//Homepage:"`). Vagy a `$this->absoluteUrls = true` beállítással úgy is beállíthatja a prezentert, hogy csak abszolút linkeket generáljon. +Abszolút URL létrehozásához adjon hozzá két kötőjelet az elejéhez (pl. `n:href="//Home:"`). Vagy a `$this->absoluteUrls = true` beállítással úgy is beállíthatja a prezentert, hogy csak abszolút linkeket generáljon. Link az aktuális oldalra .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Mivel a [komponensek |components] különálló, újrafelhasználható egységek Ha a komponenssablonban előadókra akarunk hivatkozni, akkor a `{plink}` címkét használjuk: ```latte -homepage +home ``` vagy a kódban ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/hu/how-it-works.texy b/application/hu/how-it-works.texy index d96f2efe4b..59f923ab51 100644 --- a/application/hu/how-it-works.texy +++ b/application/hu/how-it-works.texy @@ -23,10 +23,10 @@ A könyvtárszerkezet valahogy így néz ki: web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ A biztonság kedvéért próbáljuk meg az egész folyamatot egy kicsit más URL 1) az URL a következő lesz `https://example.com` 2) elindítjuk az alkalmazást, létrehozunk egy konténert és futtatjuk `Application::run()` -3) a router dekódolja az URL-t, mint egy párat `Homepage:default` -4) létrejön egy `HomepagePresenter` objektum +3) a router dekódolja az URL-t, mint egy párat `Home:default` +4) létrejön egy `HomePresenter` objektum 5) a `renderDefault()` metódust meghívjuk (ha létezik) -6) egy `templates/Homepage/default.latte` sablon `templates/@layout.latte` elrendezéssel megjelenik. +6) egy `templates/Home/default.latte` sablon `templates/@layout.latte` elrendezéssel megjelenik. Lehet, hogy most sok új fogalommal találkoztál, de úgy gondoljuk, hogy van értelme. Az alkalmazások létrehozása a Nette-ben gyerekjáték. diff --git a/application/hu/modules.texy b/application/hu/modules.texy index bb5b84f6c6..5dde2d1a36 100644 --- a/application/hu/modules.texy +++ b/application/hu/modules.texy @@ -104,7 +104,7 @@ Feltérképezés .[#toc-mapping] Meghatározza azokat a szabályokat, amelyek alapján az osztály neve az előadó nevéből származik. Ezeket a [konfigurációban |configuration] a `application › mapping` kulcs alatt írjuk le. -Kezdjük egy olyan példával, amely nem használ modulokat. Csak azt akarjuk, hogy a prezenter osztályok a `App\Presenters` névtérrel rendelkezzenek. Ez azt jelenti, hogy egy olyan prezenternek, mint a `Homepage`, a `App\Presenters\HomepagePresenter` osztályhoz kell kapcsolódnia. Ezt a következő konfigurációval érhetjük el: +Kezdjük egy olyan példával, amely nem használ modulokat. Csak azt akarjuk, hogy a prezenter osztályok a `App\Presenters` névtérrel rendelkezzenek. Ez azt jelenti, hogy egy olyan prezenternek, mint a `Home`, a `App\Presenters\HomePresenter` osztályhoz kell kapcsolódnia. Ezt a következő konfigurációval érhetjük el: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Most a `Front:Homepage` bemutatót a `App\Modules\Front\Presenters\HomepagePresenter` osztályra, a `Admin:Dashboard` bemutatót pedig a `App\Modules\Admin\Presenters\DashboardPresenter` osztályra képezzük le. +Most a `Front:Home` bemutatót a `App\Modules\Front\Presenters\HomePresenter` osztályra, a `Admin:Dashboard` bemutatót pedig a `App\Modules\Admin\Presenters\DashboardPresenter` osztályra képezzük le. Praktikusabb egy általános (csillag) szabályt létrehozni az első kettő helyett. Az extra csillagot csak a modul számára adjuk hozzá az osztálymaszkhoz: diff --git a/application/hu/routing.texy b/application/hu/routing.texy index 204915c672..ca3e0ff108 100644 --- a/application/hu/routing.texy +++ b/application/hu/routing.texy @@ -93,12 +93,12 @@ Az útvonal mostantól elfogadja a `https://any-domain.com/chronicle/` címet fo Természetesen a bemutató és az akció neve is lehet paraméter. Például: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Ez az útvonal elfogad például egy URL-t a `/article/edit` illetve `/catalog/list` formában, és lefordítja azokat a `Article:edit` illetve `Catalog:list` előadókra és műveletekre. -A `presenter` és a `action` paramétereknek alapértelmezett értékeket ad:`Homepage` és `default`, ezért ezek opcionálisak. Az útvonal tehát elfogad egy `/article` URL-t is, és lefordítja `Article:default`-ként. Vagy fordítva, a `Product:default` hivatkozás a `/product` útvonalat generálja, az alapértelmezett `Homepage:default` hivatkozás a `/` útvonalat generálja. +A `presenter` és a `action` paramétereknek alapértelmezett értékeket ad:`Home` és `default`, ezért ezek opcionálisak. Az útvonal tehát elfogad egy `/article` URL-t is, és lefordítja `Article:default`-ként. Vagy fordítva, a `Product:default` hivatkozás a `/product` útvonalat generálja, az alapértelmezett `Home:default` hivatkozás a `/` útvonalat generálja. A maszk nem csak a webhely gyökere alapján a relatív elérési utat írhatja le, hanem az abszolút elérési utat is, ha az kötőjellel kezdődik, vagy akár a teljes abszolút URL-t, ha két kötőjellel kezdődik: @@ -160,7 +160,7 @@ A szekvenciák szabadon egymásba ágyazhatók és kombinálhatók: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Elfogadott URL-címek: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); A szögletes zárójel nélküli opcionális paraméterek (azaz az alapértelmezett értékkel rendelkező paraméterek) úgy viselkednek, mintha így lennének becsomagolva: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // egyenlő: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Ha meg akarja változtatni a jobb oldali slash generálásának módját, azaz a `/homepage/` helyett egy `/homepage` kapjon, állítsa be az útvonalat így: +Ha meg akarja változtatni a jobb oldali slash generálásának módját, azaz a `/home/` helyett egy `/home` kapjon, állítsa be az útvonalat így: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Az útvonal második paramétere, amelyet gyakran a `Presenter:action` formátum ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Szűrők és fordítások .[#toc-filters-and-translations] Jó gyakorlat, hogy a forráskódot angolul írjuk, de mi van akkor, ha a weboldalunknak le kell fordítani az URL-t más nyelvre? Egyszerű útvonalak, mint például: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` angol nyelvű URL-eket generálnak, például `/product/123` vagy `/cart`. Ha azt szeretnénk, hogy az URL-ben szereplő bemutatókat és műveleteket németre fordítsák le (pl. `/produkt/123` vagy `/einkaufswagen`), használhatunk egy fordítószótárat. Ennek hozzáadásához már a második paraméter "beszédesebb" változatára van szükségünk: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // string az URL-ben => prezenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ A specifikus paraméterekhez tartozó szűrők mellett általános szűrőket is use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 A `SimpleRouter` konstruktor paramétere egy alapértelmezett bemutató és művelet, azaz a végrehajtandó művelet, ha további paraméterek nélkül megnyitjuk pl. a `http://example.com/` címet. ```php -// alapértelmezett bemutató 'Homepage' és művelet 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// alapértelmezett bemutató 'Home' és művelet 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Javasoljuk, hogy a SimpleRouter-t közvetlenül a [konfigurációban |dependency-injection:services] definiáljuk: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ A kérés feldolgozásakor legalább a prezentert és az actiont kell visszaadnu ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/hu/templates.texy b/application/hu/templates.texy index adb3369071..8f9478e63b 100644 --- a/application/hu/templates.texy +++ b/application/hu/templates.texy @@ -148,7 +148,7 @@ A sablonban az alábbiak szerint hozunk létre linkeket más előadókra és mű A `n:href` attribútum nagyon hasznos a HTML címkékhez. ``. Ha a linket máshol, például a szövegben akarjuk kiírni, akkor a `{link}`-t használjuk: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` További információért lásd: [Linkek létrehozása |Creating Links]. diff --git a/application/it/ajax.texy b/application/it/ajax.texy index aee9d22e66..1f583a4321 100644 --- a/application/it/ajax.texy +++ b/application/it/ajax.texy @@ -149,7 +149,7 @@ Non è possibile ridisegnare direttamente uno snippet dinamico (il ridisegno di Nell'esempio precedente, bisogna assicurarsi che per una richiesta AJAX venga aggiunto un solo elemento all'array `$list`, quindi il ciclo `foreach` stamperà un solo frammento dinamico. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/it/creating-links.texy b/application/it/creating-links.texy index a88d9796ea..7b8cafdf00 100644 --- a/application/it/creating-links.texy +++ b/application/it/creating-links.texy @@ -52,7 +52,7 @@ Anche i cosiddetti [parametri persistenti |presenters#persistent parameters] ven L'attributo `n:href` è molto utile per i tag HTML ``. Se vogliamo stampare il link altrove, per esempio nel testo, usiamo `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ Il formato è supportato da tutti i tag Latte e da tutti i metodi del presentato La forma base è quindi `Presenter:action`: ```latte -homepage +home ``` Se ci si collega all'azione del presentatore corrente, si può omettere il suo nome: ```latte -homepage +home ``` Se l'azione è `default`, possiamo ometterlo, ma i due punti devono rimanere: ```latte -homepage +home ``` I collegamenti possono anche puntare ad altri [moduli |modules]. In questo caso, i collegamenti si distinguono in relativi ai sottomoduli o assoluti. Il principio è analogo a quello dei percorsi su disco, solo che al posto degli slash ci sono i punti. Supponiamo che il presentatore attuale faccia parte del modulo `Front`, quindi scriveremo: @@ -119,7 +119,7 @@ Un caso particolare è il [collegamento a se stesso |#Links to Current Page]. Qu Possiamo collegarci a una determinata parte della pagina HTML tramite un cosiddetto frammento dopo il simbolo hash `#`: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Percorsi assoluti .[#toc-absolute-paths] I link generati da `link()` o `n:href` sono sempre percorsi assoluti (cioè iniziano con `/`), ma non URL assoluti con protocollo e dominio come `https://domain`. -Per generare un URL assoluto, aggiungere due barre all'inizio (ad esempio, `n:href="//Homepage:"`). Oppure si può impostare il presentatore in modo che generi solo collegamenti assoluti, impostando `$this->absoluteUrls = true`. +Per generare un URL assoluto, aggiungere due barre all'inizio (ad esempio, `n:href="//Home:"`). Oppure si può impostare il presentatore in modo che generi solo collegamenti assoluti, impostando `$this->absoluteUrls = true`. Collegamento alla pagina corrente .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Poiché i [componenti |components] sono unità riutilizzabili separate che non d Se vogliamo collegarci ai presentatori nel modello del componente, usiamo il tag `{plink}`: ```latte -homepage +home ``` o nel codice ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/it/how-it-works.texy b/application/it/how-it-works.texy index a1016563ab..aa331da3c3 100644 --- a/application/it/how-it-works.texy +++ b/application/it/how-it-works.texy @@ -23,10 +23,10 @@ La struttura delle directory è simile a questa: web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ Per sicurezza, proviamo a riepilogare l'intero processo con un URL leggermente d 1) l'URL sarà `https://example.com` 2) si avvia l'applicazione, si crea un contenitore e si esegue `Application::run()` -3) il router decodifica l'URL come una coppia di oggetti `Homepage:default` -4) viene creato un oggetto `HomepagePresenter` +3) il router decodifica l'URL come una coppia di oggetti `Home:default` +4) viene creato un oggetto `HomePresenter` 5) viene richiamato il metodo `renderDefault()` (se esiste) -6) viene reso un modello `templates/Homepage/default.latte` con un layout `templates/@layout.latte` +6) viene reso un modello `templates/Home/default.latte` con un layout `templates/@layout.latte` Potreste esservi imbattuti in molti concetti nuovi, ma crediamo che abbiano un senso. Creare applicazioni in Nette è un gioco da ragazzi. diff --git a/application/it/modules.texy b/application/it/modules.texy index 4686c49192..55ee0c0696 100644 --- a/application/it/modules.texy +++ b/application/it/modules.texy @@ -104,7 +104,7 @@ Mappatura .[#toc-mapping] Definisce le regole con cui il nome della classe viene derivato dal nome del presentatore. Vengono scritte nella [configurazione |configuration] sotto la chiave `application › mapping`. -Cominciamo con un esempio che non usa moduli. Vogliamo solo che le classi del presentatore abbiano lo spazio dei nomi `App\Presenters`. Ciò significa che un presentatore come `Homepage` deve mappare alla classe `App\Presenters\HomepagePresenter`. Questo si può ottenere con la seguente configurazione: +Cominciamo con un esempio che non usa moduli. Vogliamo solo che le classi del presentatore abbiano lo spazio dei nomi `App\Presenters`. Ciò significa che un presentatore come `Home` deve mappare alla classe `App\Presenters\HomePresenter`. Questo si può ottenere con la seguente configurazione: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Ora il presentatore `Front:Homepage` mappa alla classe `App\Modules\Front\Presenters\HomepagePresenter` e il presentatore `Admin:Dashboard` alla classe `App\Modules\Admin\Presenters\DashboardPresenter`. +Ora il presentatore `Front:Home` mappa alla classe `App\Modules\Front\Presenters\HomePresenter` e il presentatore `Admin:Dashboard` alla classe `App\Modules\Admin\Presenters\DashboardPresenter`. È più pratico creare una regola generale (asterisco) per sostituire le prime due. L'asterisco in più sarà aggiunto alla maschera di classe solo per il modulo: diff --git a/application/it/routing.texy b/application/it/routing.texy index 1ddb486642..b530d0b83b 100644 --- a/application/it/routing.texy +++ b/application/it/routing.texy @@ -93,12 +93,12 @@ La rotta accetterà ora l'URL `https://any-domain.com/chronicle/` con il paramet Naturalmente, anche il nome del presentatore e l'azione possono essere un parametro. Ad esempio: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Questo percorso accetta, ad esempio, un URL nella forma `/article/edit` e `/catalog/list` e li traduce in presentatori e azioni `Article:edit` e `Catalog:list`. -Inoltre, assegna ai parametri `presenter` e `action` i valori predefiniti`Homepage` e `default`, che sono quindi facoltativi. Quindi il percorso accetta anche un URL `/article` e lo traduce in `Article:default`. O viceversa, un link a `Product:default` genera un percorso `/product`, un link al default `Homepage:default` genera un percorso `/`. +Inoltre, assegna ai parametri `presenter` e `action` i valori predefiniti`Home` e `default`, che sono quindi facoltativi. Quindi il percorso accetta anche un URL `/article` e lo traduce in `Article:default`. O viceversa, un link a `Product:default` genera un percorso `/product`, un link al default `Home:default` genera un percorso `/`. La maschera può descrivere non solo il percorso relativo basato sulla radice del sito, ma anche il percorso assoluto quando inizia con una barra, o addirittura l'intero URL assoluto quando inizia con due barre: @@ -160,7 +160,7 @@ Le sequenze possono essere liberamente annidate e combinate: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // URL accettati: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); I parametri opzionali (cioè quelli che hanno un valore predefinito) senza parentesi quadre si comportano come se fossero avvolti in questo modo: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // equivale a: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Per cambiare il modo in cui viene generato lo slash più a destra, cioè invece di `/homepage/` ottenere un `/homepage`, regolare il percorso in questo modo: +Per cambiare il modo in cui viene generato lo slash più a destra, cioè invece di `/home/` ottenere un `/home`, regolare il percorso in questo modo: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Il secondo parametro della rotta, che spesso scriviamo nel formato `Presenter:ac ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtri e traduzioni .[#toc-filters-and-translations] È buona norma scrivere il codice sorgente in inglese, ma cosa succede se è necessario che il sito web abbia un URL tradotto in una lingua diversa? Percorsi semplici come: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` genereranno URL in inglese, come `/product/123` o `/cart`. Se vogliamo che i presenter e le azioni nell'URL siano tradotti in tedesco (per esempio `/produkt/123` o `/einkaufswagen`), possiamo usare un dizionario di traduzione. Per aggiungerlo, abbiamo già bisogno di una variante "più loquace" del secondo parametro: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // stringa nell'URL => presenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Oltre ai filtri per parametri specifici, è possibile definire anche filtri gene use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Il parametro del costruttore `SimpleRouter` è un presentatore e un'azione predefiniti, cioè l'azione da eseguire se si apre, ad esempio, `http://example.com/` senza ulteriori parametri. ```php -// default al presenter 'Homepage' e all'azione 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// default al presenter 'Home' e all'azione 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Si consiglia di definire SimpleRouter direttamente nella [configurazione |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Quando si elabora la richiesta, è necessario restituire almeno il presentatore ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/it/templates.texy b/application/it/templates.texy index 9c6cf13691..9f9da1b537 100644 --- a/application/it/templates.texy +++ b/application/it/templates.texy @@ -148,7 +148,7 @@ Nel modello si creano collegamenti ad altri presentatori e azioni come segue: L'attributo `n:href` è molto utile per i tag HTML ``. Se vogliamo stampare il link altrove, ad esempio nel testo, usiamo `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Per ulteriori informazioni, vedere [Creazione di collegamenti |Creating Links]. diff --git a/application/pl/ajax.texy b/application/pl/ajax.texy index bdeae39bd0..34eb88ff52 100644 --- a/application/pl/ajax.texy +++ b/application/pl/ajax.texy @@ -149,7 +149,7 @@ Nie można bezpośrednio unieważnić dynamicznych snippetów (unieważnienie `i W powyższym przykładzie musisz po prostu upewnić się, że gdy wykonasz żądanie ajaxowe, w zmiennej `$list` znajduje się tylko jeden wpis, a zatem, że pętla `foreach` wypełnia tylko jeden dynamiczny snippet: ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * Tato metoda vrací data pro seznam. diff --git a/application/pl/creating-links.texy b/application/pl/creating-links.texy index e87b158f61..5c9126dba0 100644 --- a/application/pl/creating-links.texy +++ b/application/pl/creating-links.texy @@ -52,7 +52,7 @@ Tak zwane [trwałe parametry |presenters#Persistent-Parameters] są również au Atrybut `n:href` jest bardzo przydatny dla znaczników HTML ``. Jeśli chcemy wymienić link w innym miejscu, na przykład w tekście, używamy `{link}`: ```latte -Adresa je: {link Homepage:default} +Adresa je: {link Home:default} ``` @@ -88,7 +88,7 @@ Format jest obsługiwany przez wszystkie znaczniki Latte oraz wszystkie metody p Podstawową formą jest więc `Presenter:action`: ```latte -úvodní stránka +úvodní stránka ``` Jeśli odnosimy się do działania bieżącego prezentera, możemy pominąć nazwę prezentera: @@ -100,7 +100,7 @@ Jeśli odnosimy się do działania bieżącego prezentera, możemy pominąć naz Jeśli celem działania jest `default`, możemy go pominąć, ale dwukropek musi pozostać: ```latte -úvodní stránka +úvodní stránka ``` Linki mogą również wskazywać na inne [moduły |modules]. Tutaj linki są rozróżniane jako względne do zagnieżdżonego podmodułu lub bezwzględne. Zasada działania jest analogiczna do ścieżek dyskowych, ale z dwukropkami zamiast ukośników. Załóżmy, że aktualny prezenter jest częścią modułu `Front`, wtedy piszemy: @@ -119,7 +119,7 @@ Szczególnym przypadkiem jest [autoreferencja |#Link-to-Current-Page], w której Możemy linkować do określonej części strony poprzez fragment po znaku siatki `#`: ```latte -odkaz na Homepage:default a fragment #main +odkaz na Home:default a fragment #main ``` @@ -128,7 +128,7 @@ Możemy linkować do określonej części strony poprzez fragment po znaku siatk Linki generowane przez `link()` lub `n:href` są zawsze ścieżkami bezwzględnymi (tj. zaczynają się od `/`), ale nie bezwzględnymi adresami URL z protokołem i domeną jak `https://domain`. -Aby wygenerować bezwzględny adres URL, dodaj dwa ukośniki na początku (np. `n:href="//Homepage:"`). Możesz też przełączyć prezenter, aby generował tylko bezwzględne linki, ustawiając `$this->absoluteUrls = true`. +Aby wygenerować bezwzględny adres URL, dodaj dwa ukośniki na początku (np. `n:href="//Home:"`). Możesz też przełączyć prezenter, aby generował tylko bezwzględne linki, ustawiając `$this->absoluteUrls = true`. Link do bieżącej strony .[#toc-link-to-current-page] @@ -165,7 +165,7 @@ Parametry są takie same jak w przypadku metody `link()`, ale dodatkowo można z Forma krótka może być stosowana w połączeniu z `n:href` w jednym elemencie: ```latte -... +... ``` Symbol wieloznaczny `*` może być użyty tylko w miejsce akcji, nie prezentera. @@ -213,13 +213,13 @@ Ponieważ [komponenty |components] są samodzielnymi, wielokrotnego użytku jedn Gdybyśmy chcieli odwołać się do prezenterów w szablonie komponentu, użylibyśmy do tego celu tagu `{plink}`: ```latte -úvod +úvod ``` lub w kodzie ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/pl/how-it-works.texy b/application/pl/how-it-works.texy index 003a54759f..84c129dd41 100644 --- a/application/pl/how-it-works.texy +++ b/application/pl/how-it-works.texy @@ -23,10 +23,10 @@ Struktura katalogów wygląda mniej więcej tak: web-project/ ├── app/ ← adresář s aplikací │ ├── Presenters/ ← presentery a šablony -│ │ ├── HomepagePresenter.php ← třída presenteru Homepage +│ │ ├── HomePresenter.php ← třída presenteru Home │ │ └── templates/ ← adresář se šablonami │ │ ├── @layout.latte ← šablona layoutu -│ │ └── Homepage/ ← šablony presenteru Homepage +│ │ └── Home/ ← šablony presenteru Home │ │ └── default.latte ← šablona akce 'default' │ ├── Router/ ← konfigurace URL adres │ └── Bootstrap.php ← zaváděcí třída Bootstrap @@ -134,10 +134,10 @@ Aby być bezpiecznym, spróbujmy podsumować cały proces z nieco innym adresem 1) Adres URL będzie następujący `https://example.com` 2) uruchamiamy aplikację, tworzymy kontener i uruchamiamy `Application::run()` -3) router dekoduje adres URL jako parę `Homepage:default` -4) tworzony jest obiekt klasy `HomepagePresenter` +3) router dekoduje adres URL jako parę `Home:default` +4) tworzony jest obiekt klasy `HomePresenter` 5) wywoływana jest metoda `renderDefault()` (jeśli istnieje) -6) wyrenderować szablon np. `templates/Homepage/default.latte` z układem np. `templates/@layout.latte` +6) wyrenderować szablon np. `templates/Home/default.latte` z układem np. `templates/@layout.latte` Teraz być może spotkałeś się z wieloma nowymi pojęciami, ale wierzymy, że mają one sens. Tworzenie aplikacji w Nette to ogromna bułka z masłem. diff --git a/application/pl/modules.texy b/application/pl/modules.texy index 632936966b..a2a4480132 100644 --- a/application/pl/modules.texy +++ b/application/pl/modules.texy @@ -104,7 +104,7 @@ Mapowanie .[#toc-mapping] Określa zasady, według których nazwa klasy jest wyprowadzana z nazwy prezentera. Zapisujemy je w [konfiguracji |configuration] pod kluczem `application › mapping`. -Zacznijmy od próbki, która nie korzysta z modułów. Będziemy chcieli, aby klasy prezentera miały przestrzeń nazw `App\Presenters`. To znaczy, będziemy chcieli, aby prezenter, na przykład, `Homepage` mapował do klasy `App\Presenters\HomepagePresenter`. Można to osiągnąć dzięki następującej konfiguracji: +Zacznijmy od próbki, która nie korzysta z modułów. Będziemy chcieli, aby klasy prezentera miały przestrzeń nazw `App\Presenters`. To znaczy, będziemy chcieli, aby prezenter, na przykład, `Home` mapował do klasy `App\Presenters\HomePresenter`. Można to osiągnąć dzięki następującej konfiguracji: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Teraz prezenter `Front:Homepage` mapuje do klasy `App\Modules\Front\Presenters\HomepagePresenter`, a prezenter `Admin:Dashboard` mapuje do klasy `App\Modules\Admin\Presenters\DashboardPresenter`. +Teraz prezenter `Front:Home` mapuje do klasy `App\Modules\Front\Presenters\HomePresenter`, a prezenter `Admin:Dashboard` mapuje do klasy `App\Modules\Admin\Presenters\DashboardPresenter`. Bardziej praktyczne będzie stworzenie ogólnej (gwiazdkowej) reguły, która zastąpi pierwsze dwie. W masce klasy zostanie dodana dodatkowa gwiazdka tylko dla tego modułu: diff --git a/application/pl/routing.texy b/application/pl/routing.texy index d0054da557..1cc006af6e 100644 --- a/application/pl/routing.texy +++ b/application/pl/routing.texy @@ -93,12 +93,12 @@ Trasa będzie teraz akceptować również adres URL `https://example.com/chronic Oczywiście parametrem może być również prezenter i nazwa wydarzenia. Na przykład: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Podana trasa przyjmuje np. adresy URL o postaci `/article/edit` lub również `/catalog/list` i rozumie je jako prezentery i wydarzenia `Article:edit` i `Catalog:list`. -Jednocześnie nadaje parametrom `presenter` i `action` wartości domyślne `Homepage` i `default`, a zatem są one również opcjonalne. Tak więc router akceptuje również adresy URL w postaci `/article` i traktuje je jako `Article:default`. Lub odwrotnie, link do `Product:default` wygeneruje ścieżkę `/product`, link do domyślnego `Homepage:default` wygeneruje ścieżkę `/`. +Jednocześnie nadaje parametrom `presenter` i `action` wartości domyślne `Home` i `default`, a zatem są one również opcjonalne. Tak więc router akceptuje również adresy URL w postaci `/article` i traktuje je jako `Article:default`. Lub odwrotnie, link do `Product:default` wygeneruje ścieżkę `/product`, link do domyślnego `Home:default` wygeneruje ścieżkę `/`. Maska może opisywać nie tylko ścieżkę względną z korzenia strony, ale także ścieżkę bezwzględną, jeśli zaczyna się od ukośnika, a nawet pełny bezwzględny adres URL, jeśli zaczyna się od dwóch ukośników: @@ -160,7 +160,7 @@ Sekwencje mogą być dowolnie osadzane i łączone: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Akceptuje cesty: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Parametry opcjonalne (tzn. parametry posiadające wartość domyślną) bez nawiasów kwadratowych zachowują się zasadniczo tak, jakby były objęte nawiasami, jak poniżej: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // odpovídá tomuto: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Jeśli chcemy wpłynąć na zachowanie ukośnika spiczastego tak, aby np. zamiast `/homepage/` generowany był tylko `/homepage`, można to zrobić w następujący sposób: +Jeśli chcemy wpłynąć na zachowanie ukośnika spiczastego tak, aby np. zamiast `/home/` generowany był tylko `/home`, można to zrobić w następujący sposób: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Drugi parametr routy, który często zapisywany jest w formacie `Presenter:actio ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtry i tłumaczenia .[#toc-filters-and-translations] Kod źródłowy aplikacji piszemy w języku angielskim, ale jeśli strona ma mieć czeski adres URL, to prosty typ routingu: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` wygeneruje angielski adres URL, taki jak `/product/123` lub `/cart`. Jeśli chcemy, aby prezentery i akcje w adresie URL były reprezentowane przez czeskie słowa (np. `/produkt/123` lub `/kosik`), możemy użyć słownika tłumaczeń. Aby go napisać, potrzebujemy bardziej "verbose" wersji drugiego parametru: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // řetězec v URL => presenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Oprócz filtrów specyficznych dla parametrów, możemy również zdefiniować f use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Parametrem konstruktora SimpleRouter jest domyślny prezenter & akcja, do której zostaniemy skierowani, jeśli otworzymy stronę bez parametrów, np. `http://example.com/`. ```php -// výchozím presenterem bude 'Homepage' a akce 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// výchozím presenterem bude 'Home' a akce 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Zalecane jest zdefiniowanie SimpleRoutera bezpośrednio w [konfiguracji |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Podczas przetwarzania żądania musimy zwrócić co najmniej prezentera i akcję ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/pl/templates.texy b/application/pl/templates.texy index 4c18b34672..9a9d31a92e 100644 --- a/application/pl/templates.texy +++ b/application/pl/templates.texy @@ -148,7 +148,7 @@ Szablon tworzy w ten sposób linki do innych prezenterów & wydarzeń: Atrybut `n:href` jest bardzo przydatny dla znaczników HTML ``. Jeśli chcemy wymienić link w innym miejscu, na przykład w tekście, używamy `{link}`: ```latte -Adresa je: {link Homepage:default} +Adresa je: {link Home:default} ``` Aby uzyskać więcej informacji, zobacz [Tworzenie linków URL |creating-links]. diff --git a/application/pt/ajax.texy b/application/pt/ajax.texy index cf4a0f988e..b4cb976fc9 100644 --- a/application/pt/ajax.texy +++ b/application/pt/ajax.texy @@ -149,7 +149,7 @@ Você não pode redesenhar um trecho dinâmico diretamente (o redesenho de `item No exemplo acima você tem que ter certeza de que para um pedido AJAX apenas um item será adicionado à matriz `$list`, portanto o laço `foreach` imprimirá apenas um trecho dinâmico. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/pt/creating-links.texy b/application/pt/creating-links.texy index df174aca3e..b58d739ab1 100644 --- a/application/pt/creating-links.texy +++ b/application/pt/creating-links.texy @@ -52,7 +52,7 @@ Os chamados [parâmetros persistentes |presenters#persistent parameters] também Atributo `n:href` é muito útil para tags HTML ``. Se quisermos imprimir o link em outro lugar, por exemplo, no texto, usamos `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ O formato é suportado por todas as etiquetas Latte e todos os métodos de apres A forma básica é, portanto, `Presenter:action`: ```latte -homepage +home ``` Se nos ligarmos à ação do atual apresentador, podemos omitir seu nome: ```latte -homepage +home ``` Se a ação é `default`, podemos omiti-la, mas o cólon deve permanecer: ```latte -homepage +home ``` Os links também podem apontar para outros [módulos |modules]. Aqui, os links são diferenciados em relativos aos submódulos, ou absolutos. O princípio é análogo aos caminhos do disco, somente em vez de cortes existem colons. Vamos supor que o apresentador real faça parte do módulo `Front`, então escreveremos: @@ -119,7 +119,7 @@ Um caso especial está [ligado a si mesmo |#Links to Current Page]. Aqui escreve Podemos criar um link para uma determinada parte da página HTML através do chamado fragmento após o símbolo `#` hash: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Caminhos Absolutos .[#toc-absolute-paths] Links gerados por `link()` ou `n:href` são sempre caminhos absolutos (ou seja, começam com `/`), mas não URLs absolutas com um protocolo e domínio como `https://domain`. -Para gerar uma URL absoluta, acrescente duas barras ao início (por exemplo, `n:href="//Homepage:"`). Ou você pode mudar o apresentador para gerar apenas links absolutos, definindo `$this->absoluteUrls = true`. +Para gerar uma URL absoluta, acrescente duas barras ao início (por exemplo, `n:href="//Home:"`). Ou você pode mudar o apresentador para gerar apenas links absolutos, definindo `$this->absoluteUrls = true`. Link para a página atual .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Como [os componentes |components] são unidades reutilizáveis separadas que nã Se quisermos fazer um link para apresentadores no modelo de componente, usamos a tag `{plink}`: ```latte -homepage +home ``` ou no código ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/pt/how-it-works.texy b/application/pt/how-it-works.texy index 596ea9373b..a4e6915e34 100644 --- a/application/pt/how-it-works.texy +++ b/application/pt/how-it-works.texy @@ -23,10 +23,10 @@ A estrutura do diretório é algo parecido com isto: web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ Só para ter certeza, vamos tentar recapitular todo o processo com uma URL ligei 1) a URL será `https://example.com` 2) iniciamos a aplicação, criamos um container e executamos `Application::run()` -3) o roteador decodifica a URL como um par `Homepage:default` -4) um objeto `HomepagePresenter` é criado +3) o roteador decodifica a URL como um par `Home:default` +4) um objeto `HomePresenter` é criado 5) método `renderDefault()` é chamado (se existir) -6) um modelo `templates/Homepage/default.latte` com um layout `templates/@layout.latte` é apresentado +6) um modelo `templates/Home/default.latte` com um layout `templates/@layout.latte` é apresentado Você pode ter se deparado com muitos conceitos novos agora, mas acreditamos que eles fazem sentido. Criar aplicações em Nette é uma brisa. diff --git a/application/pt/modules.texy b/application/pt/modules.texy index fbabdb0113..35ee3786d3 100644 --- a/application/pt/modules.texy +++ b/application/pt/modules.texy @@ -104,7 +104,7 @@ Mapeamento .[#toc-mapping] Define as regras pelas quais o nome da classe é derivado do nome do apresentador. Nós as escrevemos na [configuração |configuration] sob a chave `application › mapping`. -Vamos começar com uma amostra que não utiliza módulos. Queremos apenas que as classes de apresentadores tenham o namespace `App\Presenters`. Isso significa que um apresentador como o `Homepage` deve mapear para a classe `App\Presenters\HomepagePresenter`. Isto pode ser conseguido através da seguinte configuração: +Vamos começar com uma amostra que não utiliza módulos. Queremos apenas que as classes de apresentadores tenham o namespace `App\Presenters`. Isso significa que um apresentador como o `Home` deve mapear para a classe `App\Presenters\HomePresenter`. Isto pode ser conseguido através da seguinte configuração: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Agora o apresentador `Front:Homepage` mapeia para a classe `App\Modules\Front\Presenters\HomepagePresenter` e o apresentador `Admin:Dashboard` para a classe `App\Modules\Admin\Presenters\DashboardPresenter`. +Agora o apresentador `Front:Home` mapeia para a classe `App\Modules\Front\Presenters\HomePresenter` e o apresentador `Admin:Dashboard` para a classe `App\Modules\Admin\Presenters\DashboardPresenter`. É mais prático criar uma regra geral (estrela) para substituir as duas primeiras. O asterisco extra será adicionado à máscara de classe apenas para o módulo: diff --git a/application/pt/routing.texy b/application/pt/routing.texy index 23679faf82..d9ad4462b0 100644 --- a/application/pt/routing.texy +++ b/application/pt/routing.texy @@ -93,12 +93,12 @@ A rota agora aceitará a URL `https://any-domain.com/chronicle/` com o parâmetr Naturalmente, o nome do apresentador e a ação também podem ser um parâmetro. Por exemplo, o nome do apresentador e a ação também podem ser um parâmetro: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Esta rota aceita, por exemplo, uma URL no formulário `/article/edit` resp. `/catalog/list` e as traduz para os apresentadores e ações `Article:edit` resp. `Catalog:list`. -Também dá aos parâmetros `presenter` e `action` valores padrão`Homepage` e `default` e, portanto, são opcionais. Portanto, a rota também aceita uma URL `/article` e a traduz como `Article:default`. Ou vice versa, um link para `Product:default` gera um caminho `/product`, um link para o padrão `Homepage:default` gera um caminho `/`. +Também dá aos parâmetros `presenter` e `action` valores padrão`Home` e `default` e, portanto, são opcionais. Portanto, a rota também aceita uma URL `/article` e a traduz como `Article:default`. Ou vice versa, um link para `Product:default` gera um caminho `/product`, um link para o padrão `Home:default` gera um caminho `/`. A máscara pode descrever não apenas o caminho relativo baseado na raiz do site, mas também o caminho absoluto quando ele começa com uma barra, ou mesmo todo o URL absoluto quando começa com duas barras: @@ -160,7 +160,7 @@ As seqüências podem ser livremente aninhadas e combinadas: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // URLs aceitas: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Os parâmetros opcionais (ou seja, parâmetros com valor padrão) sem parênteses rectos comportam-se como se fossem embrulhados desta forma: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // é igual a: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Para mudar a forma como a barra mais à direita é gerada, ou seja, ao invés de `/homepage/`, obtenha um `/homepage`, ajuste a rota desta forma: +Para mudar a forma como a barra mais à direita é gerada, ou seja, ao invés de `/home/`, obtenha um `/home`, ajuste a rota desta forma: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ O segundo parâmetro da rota, que freqüentemente escrevemos no formato `Present ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtros e Traduções .[#toc-filters-and-translations] É uma boa prática escrever o código fonte em inglês, mas e se você precisar que seu website tenha o URL traduzido para outro idioma? Rotas simples, como por exemplo: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` gerará URLs em inglês, tais como `/product/123` ou `/cart`. Se quisermos ter apresentadores e ações na URL traduzidas para o Deutsch (por exemplo `/produkt/123` ou `/einkaufswagen`), podemos usar um dicionário de tradução. Para adicioná-lo, já precisamos de uma variante "mais faladora" do segundo parâmetro: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // string na URL => apresentador 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Além dos filtros para parâmetros específicos, você também pode definir filt use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 O parâmetro do construtor `SimpleRouter` é um apresentador e ação padrão, ou seja, ação a ser executada se abrirmos, por exemplo, `http://example.com/` sem parâmetros adicionais. ```php -// default para o apresentador 'Homepage' e ação 'default -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// default para o apresentador 'Home' e ação 'default +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Recomendamos definir o SimpleRouter diretamente na [configuração |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Ao processar o pedido, devemos retornar pelo menos o apresentador e a ação. O ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/pt/templates.texy b/application/pt/templates.texy index bd63ac37d7..bf8e08a5ef 100644 --- a/application/pt/templates.texy +++ b/application/pt/templates.texy @@ -148,7 +148,7 @@ No modelo, criamos links para outros apresentadores e ações da seguinte forma: O atributo `n:href` é muito útil para tags HTML ``. Se quisermos imprimir o link em outro lugar, por exemplo, no texto, usamos `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Para mais informações, consulte [Criação de links |Creating Links]. diff --git a/application/ro/ajax.texy b/application/ro/ajax.texy index 3c1de634a7..78691c1798 100644 --- a/application/ro/ajax.texy +++ b/application/ro/ajax.texy @@ -149,7 +149,7 @@ Nu puteți redesena direct un fragment dinamic (redesenarea lui `item-1` nu are În exemplul de mai sus, trebuie să vă asigurați că, pentru o cerere AJAX, doar un singur element va fi adăugat la matricea `$list`, prin urmare, bucla `foreach` va imprima doar un singur fragment dinamic. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/ro/creating-links.texy b/application/ro/creating-links.texy index 1b4d1fd1d4..659cc88a08 100644 --- a/application/ro/creating-links.texy +++ b/application/ro/creating-links.texy @@ -52,7 +52,7 @@ Așa-numiții [parametri persistenți |presenters#persistent parameters] sunt, d Atributul `n:href` este foarte util pentru etichetele HTML ``. Dacă dorim să imprimăm link-ul în altă parte, de exemplu în text, folosim `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ Formatul este acceptat de toate etichetele Latte și de toate metodele presenter Forma de bază este, prin urmare, `Presenter:action`: ```latte -homepage +home ``` Dacă facem legătura cu acțiunea prezentatorului curent, putem omite numele acestuia: ```latte -homepage +home ``` Dacă acțiunea este `default`, putem omite numele, dar trebuie să păstrăm două puncte: ```latte -homepage +home ``` Legăturile pot indica și alte [module |modules]. Aici, legăturile se disting în relative la submodule sau absolute. Principiul este analog cu cel al căilor de acces pe disc, doar că în loc de bară oblică se folosesc două puncte. Să presupunem că actualul prezentator face parte din modulul `Front`, atunci vom scrie: @@ -119,7 +119,7 @@ Un caz special este [legătura către sine |#Links to Current Page]. Aici vom sc Putem crea o legătură către o anumită parte a paginii HTML prin intermediul unui așa-numit fragment după simbolul hash `#`: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Căi de acces absolute .[#toc-absolute-paths] Legăturile generate de `link()` sau `n:href` sunt întotdeauna căi de acces absolute (adică încep cu `/`), dar nu și URL-uri absolute cu un protocol și domeniu, cum ar fi `https://domain`. -Pentru a genera o adresă URL absolută, adăugați două bariere la început (de exemplu, `n:href="//Homepage:"`). Sau puteți comuta prezentatorul pentru a genera numai link-uri absolute, setând `$this->absoluteUrls = true`. +Pentru a genera o adresă URL absolută, adăugați două bariere la început (de exemplu, `n:href="//Home:"`). Sau puteți comuta prezentatorul pentru a genera numai link-uri absolute, setând `$this->absoluteUrls = true`. Legătură către pagina curentă .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Deoarece [componentele |components] sunt unități separate reutilizabile care n Dacă dorim să facem legătura cu prezentatorii din șablonul componentei, folosim eticheta `{plink}`: ```latte -homepage +home ``` sau în cod ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/ro/how-it-works.texy b/application/ro/how-it-works.texy index acecb91475..eeb65bb9f7 100644 --- a/application/ro/how-it-works.texy +++ b/application/ro/how-it-works.texy @@ -23,10 +23,10 @@ Structura directoarelor arată cam așa: web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ Doar pentru a fi siguri, să încercăm să recapitulăm întregul proces cu un 1) URL-ul va fi `https://example.com` 2) vom porni aplicația, vom crea un container și vom rula `Application::run()` -3) routerul decodifică URL-ul ca o pereche `Homepage:default` -4) este creat un obiect `HomepagePresenter` +3) routerul decodifică URL-ul ca o pereche `Home:default` +4) este creat un obiect `HomePresenter` 5) se apelează metoda `renderDefault()` (dacă există) -6) este redat un șablon `templates/Homepage/default.latte` cu un layout `templates/@layout.latte` +6) este redat un șablon `templates/Home/default.latte` cu un layout `templates/@layout.latte` Este posibil să fi întâlnit acum o mulțime de concepte noi, dar noi credem că acestea au sens. Crearea de aplicații în Nette este o joacă de copii. diff --git a/application/ro/modules.texy b/application/ro/modules.texy index d9a6513d73..bc8e11b0d2 100644 --- a/application/ro/modules.texy +++ b/application/ro/modules.texy @@ -104,7 +104,7 @@ Cartografiere .[#toc-mapping] Definește regulile prin care numele clasei este derivat din numele prezentatorului. Le scriem în [configurație |configuration] sub cheia `application › mapping`. -Să începem cu un exemplu care nu folosește module. Vom dori doar ca clasele de prezentator să aibă spațiul de nume `App\Presenters`. Aceasta înseamnă că un prezentator precum `Homepage` ar trebui să se mapeze la clasa `App\Presenters\HomepagePresenter`. Acest lucru poate fi realizat prin următoarea configurație: +Să începem cu un exemplu care nu folosește module. Vom dori doar ca clasele de prezentator să aibă spațiul de nume `App\Presenters`. Aceasta înseamnă că un prezentator precum `Home` ar trebui să se mapeze la clasa `App\Presenters\HomePresenter`. Acest lucru poate fi realizat prin următoarea configurație: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Acum, prezentatorul `Front:Homepage` se referă la clasa `App\Modules\Front\Presenters\HomepagePresenter` și prezentatorul `Admin:Dashboard` la clasa `App\Modules\Admin\Presenters\DashboardPresenter`. +Acum, prezentatorul `Front:Home` se referă la clasa `App\Modules\Front\Presenters\HomePresenter` și prezentatorul `Admin:Dashboard` la clasa `App\Modules\Admin\Presenters\DashboardPresenter`. Este mai practic să creăm o regulă generală (stea) care să le înlocuiască pe primele două. Asteriscul suplimentar va fi adăugat la masca clasei doar pentru modul: diff --git a/application/ro/routing.texy b/application/ro/routing.texy index 9a99de408f..0879cd0109 100644 --- a/application/ro/routing.texy +++ b/application/ro/routing.texy @@ -93,12 +93,12 @@ Ruta va accepta acum URL-ul `https://any-domain.com/chronicle/` cu parametrul `y Bineînțeles, numele prezentatorului și al acțiunii pot fi, de asemenea, un parametru. De exemplu: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Această rută acceptă, de exemplu, un URL de forma `/article/edit` resp. `/catalog/list` și le traduce în prezentatorii și acțiunile `Article:edit` resp. `Catalog:list`. -De asemenea, oferă parametrilor `presenter` și `action` valori implicite`Homepage` și `default` și, prin urmare, aceștia sunt opționali. Astfel, ruta acceptă și un URL `/article` și îl traduce ca `Article:default`. Sau invers, un link către `Product:default` generează o cale `/product`, un link către `Homepage:default` implicit generează o cale `/`. +De asemenea, oferă parametrilor `presenter` și `action` valori implicite`Home` și `default` și, prin urmare, aceștia sunt opționali. Astfel, ruta acceptă și un URL `/article` și îl traduce ca `Article:default`. Sau invers, un link către `Product:default` generează o cale `/product`, un link către `Home:default` implicit generează o cale `/`. Masca poate descrie nu numai calea relativă bazată pe rădăcina site-ului, ci și calea absolută atunci când începe cu o bară oblică, sau chiar întregul URL absolut atunci când începe cu două bare oblice: @@ -160,7 +160,7 @@ Secvențele pot fi liber imbricate și combinate: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // URL-uri acceptate: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Parametrii opționali (adică parametrii cu valoare implicită) fără paranteze pătrate se comportă ca și cum ar fi înfășurați astfel: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // este egal cu: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Pentru a schimba modul în care este generată cea mai din dreapta bară oblică, adică în loc de `/homepage/` obțineți un `/homepage`, ajustați traseul în acest fel: +Pentru a schimba modul în care este generată cea mai din dreapta bară oblică, adică în loc de `/home/` obțineți un `/home`, ajustați traseul în acest fel: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Al doilea parametru al traseului, pe care îl scriem adesea în formatul `Presen ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtre și traduceri .[#toc-filters-and-translations] Este o bună practică să scrieți codul sursă în limba engleză, dar ce se întâmplă dacă aveți nevoie ca site-ul dvs. să aibă URL-ul tradus în altă limbă? Rute simple, cum ar fi: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` va genera URL-uri în limba engleză, cum ar fi `/product/123` sau `/cart`. Dacă dorim ca prezentatorii și acțiunile din URL să fie traduse în limba germană (de exemplu, `/produkt/123` sau `/einkaufswagen`), putem utiliza un dicționar de traducere. Pentru a-l adăuga, avem deja nevoie de o variantă "mai vorbăreață" a celui de-al doilea parametru: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // șir de caractere în URL => prezentator 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Filtre generale .[#toc-general-filters] use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Parametrul constructorului `SimpleRouter` este un prezentator și o acțiune implicită, adică acțiunea care va fi executată dacă deschidem, de exemplu, `http://example.com/` fără parametri suplimentari. ```php -// implicit la prezentatorul "Homepage" și acțiunea "default -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// implicit la prezentatorul "Home" și acțiunea "default +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Vă recomandăm să definiți SimpleRouter direct în [configurare |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Atunci când procesăm cererea, trebuie să returnăm cel puțin prezentatorul ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/ro/templates.texy b/application/ro/templates.texy index c4be035160..4e058192af 100644 --- a/application/ro/templates.texy +++ b/application/ro/templates.texy @@ -148,7 +148,7 @@ Crearea legăturilor .[#toc-creating-links] Atributul `n:href` este foarte util pentru etichetele HTML ``. Dacă dorim să imprimăm link-ul în altă parte, de exemplu în text, folosim `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Pentru mai multe informații, consultați [Crearea de linkuri |Creating Links]. diff --git a/application/ru/ajax.texy b/application/ru/ajax.texy index 90acd53800..05cb303614 100644 --- a/application/ru/ajax.texy +++ b/application/ru/ajax.texy @@ -149,7 +149,7 @@ $this->isControlInvalid('footer'); // -> true В приведенном примере необходимо убедиться, что при AJAX-запросе в массив `$list` будет добавлен только один элемент, поэтому цикл `foreach` будет выводить только один динамический фрагмент. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * Этот метод возвращает данные для списка. diff --git a/application/ru/creating-links.texy b/application/ru/creating-links.texy index c7e3fc466b..3a4dbb4805 100644 --- a/application/ru/creating-links.texy +++ b/application/ru/creating-links.texy @@ -52,7 +52,7 @@ Атрибут `n:href` очень удобен для HTML-тегов ``. Если мы хотим вывести ссылку в другом месте, например, в тексте, мы используем `{link}`: ```latte -URL: {link Homepage:default} +URL: {link Home:default} ``` @@ -88,7 +88,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Поэтому основной формой является `Presenter:action`: ```latte -главная страница +главная страница ``` Если мы ссылаемся на действие текущего презентера, мы можем опустить его имя: @@ -100,7 +100,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Если действие является `default`, мы можем опустить его, но двоеточие должно остаться: ```latte -главная страница +главная страница ``` Ссылки могут также указывать на другие [модули |modules]. Здесь ссылки различаются на относительные по отношению к подмодулям или абсолютные. Принцип аналогичен дисковым путям, только вместо косых черт стоят двоеточия. Предположим, что настоящий презентер является частью модуля `Front`, тогда мы напишем: @@ -119,7 +119,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Мы можем ссылаться на определенную часть HTML-страницы через так называемый фрагмент после хэш-символа `#`: ```latte -ссылка на Homepage:default и фрагмент #main +ссылка на Home:default и фрагмент #main ``` @@ -128,7 +128,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Ссылки, генерируемые `link()` или `n:href`, всегда являются абсолютными путями (т.е. начинаются с `/`), но не абсолютными URL с протоколом и доменом, как `https://domain`. -Чтобы создать абсолютный URL, добавьте две косые черты в начало (например, `n:href="//Homepage:"`). Или вы можете переключить презентатор на генерацию только абсолютных ссылок, установив `$this->absoluteUrls = true`. +Чтобы создать абсолютный URL, добавьте две косые черты в начало (например, `n:href="//Home:"`). Или вы можете переключить презентатор на генерацию только абсолютных ссылок, установив `$this->absoluteUrls = true`. Ссылка на текущую страницу .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Если мы хотим сделать ссылку на презентеры в шаблоне компонента, мы используем тег `{plink}`: ```latte -главная страница +главная страница ``` or in the code ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/ru/how-it-works.texy b/application/ru/how-it-works.texy index 6b2a22a899..b5b91aa685 100644 --- a/application/ru/how-it-works.texy +++ b/application/ru/how-it-works.texy @@ -23,10 +23,10 @@ web-project/ ├── app/ ← каталог с приложением │ ├── Presenters/ ← классы презентеров -│ │ ├── HomepagePresenter.php ← Класс презентера главной страницы +│ │ ├── HomePresenter.php ← Класс презентера главной страницы │ │ └── templates/ ← директория шаблонов │ │ ├── @layout.latte ← шаблон общего макета -│ │ └── Homepage/ ← шаблоны презентера главной страницы +│ │ └── Home/ ← шаблоны презентера главной страницы │ │ └── default.latte ← шаблон действия `default` │ ├── Router/ ← конфигурация URL-адресов │ └── Bootstrap.php ← загрузочный класс Bootstrap @@ -134,10 +134,10 @@ class ProductPresenter extends Nette\Application\UI\Presenter 1) URL будет `https://example.com` 2) мы загружаем приложение, создаем контейнер и запускаем `Application::run()` -3) маршрутизатор декодирует URL как пару `Homepage:default` -4) создается объект `HomepagePresenter` +3) маршрутизатор декодирует URL как пару `Home:default` +4) создается объект `HomePresenter` 5) вызывается метод `renderDefault()` (если существует) -6) шаблон `templates/Homepage/default.latte` с макетом `templates/@layout.latte` отрисован +6) шаблон `templates/Home/default.latte` с макетом `templates/@layout.latte` отрисован Возможно, сейчас вы столкнулись с множеством новых понятий, но мы считаем, что они имеют смысл. Создавать приложения в Nette — проще простого. diff --git a/application/ru/modules.texy b/application/ru/modules.texy index 3a970717cf..b8d3e626c2 100644 --- a/application/ru/modules.texy +++ b/application/ru/modules.texy @@ -104,7 +104,7 @@ class DashboardPresenter extends Nette\Application\UI\Presenter Определяет правила, по которым имя класса выводится из имени ведущего. Мы записываем их в [конфигурацию |configuration] под ключом `application › mapping`. -Начнем с примера, в котором не используются модули. Мы просто хотим, чтобы классы ведущего имели пространство имен `App\Presenters`. То есть мы хотим, чтобы ведущий, например, `Homepage` отображался на класс `App\Presenters\HomepagePresenter`. Этого можно достичь с помощью следующей конфигурации: +Начнем с примера, в котором не используются модули. Мы просто хотим, чтобы классы ведущего имели пространство имен `App\Presenters`. То есть мы хотим, чтобы ведущий, например, `Home` отображался на класс `App\Presenters\HomePresenter`. Этого можно достичь с помощью следующей конфигурации: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Теперь презентер `Front:Homepage` определяется классом `App\Modules\Front\HomepagePresenter`, а презентер `Admin:Dashboard` — `App\AdminModule\DashboardPresenter`. +Теперь презентер `Front:Home` определяется классом `App\Modules\Front\HomePresenter`, а презентер `Admin:Dashboard` — `App\AdminModule\DashboardPresenter`. Удобнее будет создать общее правило (звездочка), которое заменит первые два правила и добавит дополнительную звездочку только для модуля: diff --git a/application/ru/routing.texy b/application/ru/routing.texy index 3e41696e64..5bff8b8035 100644 --- a/application/ru/routing.texy +++ b/application/ru/routing.texy @@ -93,12 +93,12 @@ $router->addRoute('chronicle/', 'History:show'); Конечно, имя презентера и действие также могут быть параметрами. Например: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Этот маршрут принимает, например, URL в форме `/article/edit` и `/catalog/list` соответственно, и переводит их в презентеры и действия `Article:edit` и `Catalog:list` соответственно. -Он также придает параметрам `presenter` и `action` значения по умолчанию `Homepage` и `default` и поэтому они являются необязательными. Поэтому маршрут также принимает URL `/article` и переводит его как `Article:default`. Или наоборот, ссылка на `Product:default` генерирует путь `/product`, ссылка на стандартную `Homepage:default` генерирует путь `/`. +Он также придает параметрам `presenter` и `action` значения по умолчанию `Home` и `default` и поэтому они являются необязательными. Поэтому маршрут также принимает URL `/article` и переводит его как `Article:default`. Или наоборот, ссылка на `Product:default` генерирует путь `/product`, ссылка на стандартную `Home:default` генерирует путь `/`. Маска может описывать не только относительный путь, основанный на корне сайта, но и абсолютный путь, если он начинается со слэша, или даже весь абсолютный URL, если он начинается с двух слэшей: @@ -160,7 +160,7 @@ $router->addRoute('//[.]example.com//', /* ... */); ```php $router->addRoute( '[[-]/][page-]', - 'Homepage:default', + 'Home:default', ); // Принимаемые URL-адреса: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Необязательные параметры (т. е. параметры, имеющие значение по умолчанию) без квадратных скобок ведут себя так, как если бы они были обернуты таким образом: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // equals to: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Чтобы изменить способ генерации самой правой косой черты, т. е. вместо `/homepage/` получить `/homepage`, настройте маршрут таким образом: +Чтобы изменить способ генерации самой правой косой черты, т. е. вместо `/home/` получить `/home`, настройте маршрут таким образом: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ $router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ $router->addRoute('/[/]', [ Хорошей практикой является написание исходного кода на английском языке, но что если вам нужно, чтобы URL вашего сайта был переведен на другой язык? ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` будет генерировать английские URL, такие как `/product/123` или `/cart`. Если мы хотим, чтобы презентеры и действия в URL были переведены на немецкий язык (например, `/produkt/123` или `/einkaufswagen`), мы можем использовать словарь переводов. Чтобы добавить его, нам уже нужен «более понятный» вариант второго параметра: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // строка в URL => ведущий 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ $router->addRoute('//', [ use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Параметром конструктора `SimpleRouter` является презентер и действие по умолчанию, т. е. действие, которое будет выполнено, если мы откроем, например, `http://example.com/` без дополнительных параметров. ```php -// используем презентер 'Homepage' и действие 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// используем презентер 'Home' и действие 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Мы рекомендуем определять SimpleRouter непосредственно в [конфигурации |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ class MyRouter implements Nette\Routing\Router ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/ru/templates.texy b/application/ru/templates.texy index 046b77987c..25333ec9a1 100644 --- a/application/ru/templates.texy +++ b/application/ru/templates.texy @@ -148,7 +148,7 @@ public function renderDefault(): void Атрибут `n:href` очень удобен для HTML-тегов. ``. Если мы хотим указать ссылку в другом месте, например, в тексте, мы используем `{link}`: ```latte -Adresa je: {link Homepage:default} +Adresa je: {link Home:default} ``` Дополнительные сведения см. в разделе [Создание ссылок URL |creating-links]. diff --git a/application/sl/ajax.texy b/application/sl/ajax.texy index da623b80c7..a30819a9a5 100644 --- a/application/sl/ajax.texy +++ b/application/sl/ajax.texy @@ -149,7 +149,7 @@ Dinamičnega utrinka ne morete narisati neposredno (ponovno narisanje `item-1` n V zgornjem primeru morate poskrbeti, da bo za zahtevo AJAX v polje `$list` dodan samo en element, zato bo zanka `foreach` izpisala samo en dinamični odlomek. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/sl/creating-links.texy b/application/sl/creating-links.texy index 5db6afbcb0..4943feeb98 100644 --- a/application/sl/creating-links.texy +++ b/application/sl/creating-links.texy @@ -52,7 +52,7 @@ V povezavah se samodejno posredujejo tudi tako imenovani [trajni parametri |pres Atribut `n:href` je zelo priročen za oznake HTML ``. Če želimo povezavo izpisati drugje, na primer v besedilu, uporabimo `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ To obliko podpirajo vse oznake Latte in vse metode presenterja, ki delajo s pove Osnovna oblika je torej `Presenter:action`: ```latte -homepage +home ``` Če se povežemo z dejanjem trenutnega predstavnika, lahko izpustimo njegovo ime: ```latte -homepage +home ``` Če je dejanje `default`, ga lahko izpustimo, vendar mora dvopičje ostati: ```latte -homepage +home ``` Povezave lahko kažejo tudi na druge [module |modules]. Tu se povezave razlikujejo na relativne na podmodule ali absolutne. Načelo je podobno kot pri diskovnih poteh, le da so namesto poševnic dvopičja. Predpostavimo, da je dejanski predstavnik del modula `Front`, potem bomo zapisali: @@ -119,7 +119,7 @@ Poseben primer je [povezovanje na samega sebe |#Links to Current Page]. V tem pr Na določen del strani HTML se lahko povežemo s tako imenovanim fragmentom za simbolom `#` hash: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Absolutne poti .[#toc-absolute-paths] Povezave, ki jih generirata `link()` ali `n:href`, so vedno absolutne poti (tj. začnejo se z `/`), ne pa tudi absolutni naslovi URL s protokolom in domeno, kot `https://domain`. -Če želite ustvariti absolutni naslov URL, na začetek dodajte dve poševnici (npr. `n:href="//Homepage:"`). Lahko pa tudi preklopite predstavnik, da ustvarja samo absolutne povezave, tako da nastavite `$this->absoluteUrls = true`. +Če želite ustvariti absolutni naslov URL, na začetek dodajte dve poševnici (npr. `n:href="//Home:"`). Lahko pa tudi preklopite predstavnik, da ustvarja samo absolutne povezave, tako da nastavite `$this->absoluteUrls = true`. Povezava na trenutno stran .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Ker so [komponente |components] ločene enote za večkratno uporabo, ki naj ne b Če se želimo v predlogi komponente povezati s predstavniki, uporabimo oznako `{plink}`: ```latte -homepage +home ``` ali v kodi ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/sl/how-it-works.texy b/application/sl/how-it-works.texy index 75913cfb5f..ed6745881b 100644 --- a/application/sl/how-it-works.texy +++ b/application/sl/how-it-works.texy @@ -23,10 +23,10 @@ Struktura imenikov je videti nekako takole: web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ Da bi se prepričali, poskusite celoten postopek ponoviti z nekoliko drugačnim 1) naslov URL bo `https://example.com` 2) zaženemo aplikacijo, ustvarimo vsebnik in zaženemo `Application::run()` -3) usmerjevalnik dekodira naslov URL kot par `Homepage:default` -4) ustvari se objekt `HomepagePresenter` +3) usmerjevalnik dekodira naslov URL kot par `Home:default` +4) ustvari se objekt `HomePresenter` 5) kličemo metodo `renderDefault()` (če obstaja) -6) prikaže se predloga `templates/Homepage/default.latte` z razporeditvijo `templates/@layout.latte` +6) prikaže se predloga `templates/Home/default.latte` z razporeditvijo `templates/@layout.latte` Morda ste zdaj naleteli na veliko novih konceptov, vendar verjamemo, da so smiselni. Ustvarjanje aplikacij v programu Nette je zelo enostavno. diff --git a/application/sl/modules.texy b/application/sl/modules.texy index 39f7ce0d31..d37ea342b1 100644 --- a/application/sl/modules.texy +++ b/application/sl/modules.texy @@ -104,7 +104,7 @@ Kartiranje .[#toc-mapping] Določa pravila, po katerih se ime razreda izpelje iz imena predstavnika. Zapišemo jih v [konfiguracijo |configuration] pod ključ `application › mapping`. -Začnimo z vzorcem, ki ne uporablja modulov. Želeli bomo le, da imajo razredi predstavnikov imenski prostor `App\Presenters`. To pomeni, da mora biti predstavnik, kot je `Homepage`, preslikan v razred `App\Presenters\HomepagePresenter`. To lahko dosežemo z naslednjo konfiguracijo: +Začnimo z vzorcem, ki ne uporablja modulov. Želeli bomo le, da imajo razredi predstavnikov imenski prostor `App\Presenters`. To pomeni, da mora biti predstavnik, kot je `Home`, preslikan v razred `App\Presenters\HomePresenter`. To lahko dosežemo z naslednjo konfiguracijo: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Sedaj je predstavnik `Front:Homepage` preslikan v razred `App\Modules\Front\Presenters\HomepagePresenter`, predstavnik `Admin:Dashboard` pa v razred `App\Modules\Admin\Presenters\DashboardPresenter`. +Sedaj je predstavnik `Front:Home` preslikan v razred `App\Modules\Front\Presenters\HomePresenter`, predstavnik `Admin:Dashboard` pa v razred `App\Modules\Admin\Presenters\DashboardPresenter`. Bolj praktično je ustvariti splošno (zvezdno) pravilo, ki bo nadomestilo prvi dve. Dodatna zvezdica bo dodana maski razreda samo za ta modul: diff --git a/application/sl/routing.texy b/application/sl/routing.texy index ec238884e4..046c205488 100644 --- a/application/sl/routing.texy +++ b/application/sl/routing.texy @@ -93,12 +93,12 @@ Pot bo zdaj sprejela naslov URL `https://any-domain.com/chronicle/` s parametrom Seveda sta lahko parametra tudi ime predstavnika in akcija. Na primer: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Ta pot sprejme na primer naslov URL v obliki `/article/edit` oz. `/catalog/list` in ga prevede v predstavnike in dejanja `Article:edit` oz. `Catalog:list`. -Parametroma `presenter` in `action` daje tudi privzete vrednosti`Homepage` in `default`, zato sta neobvezna. Tako pot sprejme tudi naslov URL `/article` in ga prevede kot `Article:default`. Ali obratno, povezava na `Product:default` ustvari pot `/product`, povezava na privzeto `Homepage:default` pa ustvari pot `/`. +Parametroma `presenter` in `action` daje tudi privzete vrednosti`Home` in `default`, zato sta neobvezna. Tako pot sprejme tudi naslov URL `/article` in ga prevede kot `Article:default`. Ali obratno, povezava na `Product:default` ustvari pot `/product`, povezava na privzeto `Home:default` pa ustvari pot `/`. Maska lahko opiše ne le relativno pot na podlagi korena spletnega mesta, temveč tudi absolutno pot, kadar se začne s poševnico, ali celo celoten absolutni naslov URL, kadar se začne z dvema poševnicama: @@ -160,7 +160,7 @@ Sekvence se lahko poljubno gnezdijo in kombinirajo: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Sprejeti naslovi URL: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Izbirni parametri (tj. parametri s privzeto vrednostjo) brez oglatih oklepajev se obnašajo, kot da bi bili zaviti na ta način: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // je enako: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Če želite spremeniti način generiranja skrajne desne poševnice, tj. namesto `/homepage/` dobite `/homepage`, prilagodite pot na ta način: +Če želite spremeniti način generiranja skrajne desne poševnice, tj. namesto `/home/` dobite `/home`, prilagodite pot na ta način: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Drugi parameter poti, ki ga pogosto zapišemo v obliki `Presenter:action`, je ok ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtri in prevodi .[#toc-filters-and-translations] Dobra praksa je, da izvorno kodo pišete v angleščini, a kaj, če potrebujete, da je URL vašega spletnega mesta preveden v drug jezik? Preproste poti, kot so npr: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` bodo ustvarile angleške naslove URL, kot sta `/product/123` ali `/cart`. Če želimo, da so predstavniki in dejanja v naslovu URL prevedeni v nemščino (npr. `/produkt/123` ali `/einkaufswagen`), lahko uporabimo prevajalski slovar. Za njegovo dodajanje že potrebujemo "bolj zgovorno" različico drugega parametra: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // niz v naslovu URL => presenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Poleg filtrov za določene parametre lahko določite tudi splošne filtre, ki pr use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Parameter konstruktorja `SimpleRouter` je privzeti predstavnik in dejanje, tj. dejanje, ki se izvede, če odpremo npr. `http://example.com/` brez dodatnih parametrov. ```php -// privzeto za predstavitelja 'Homepage' in akcijo 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// privzeto za predstavitelja 'Home' in akcijo 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Priporočamo, da se SimpleRouter opredeli neposredno v [konfiguraciji |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ Pri obdelavi zahteve moramo vrniti vsaj predstavnika in akcijo. Ime predstavnika ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/sl/templates.texy b/application/sl/templates.texy index aacc758b1c..2c34a4a33b 100644 --- a/application/sl/templates.texy +++ b/application/sl/templates.texy @@ -148,7 +148,7 @@ V predlogi ustvarimo povezave do drugih predstavnikov in akcij na naslednji nač Atribut `n:href` je zelo priročen za oznake HTML ``. Če želimo povezavo natisniti drugje, na primer v besedilu, uporabimo `{link}`: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Za več informacij glejte [Ustvarjanje povezav |Creating Links]. diff --git a/application/tr/ajax.texy b/application/tr/ajax.texy index 9a1ff115fc..e40b00fd9a 100644 --- a/application/tr/ajax.texy +++ b/application/tr/ajax.texy @@ -149,7 +149,7 @@ Dinamik bir snippet'i doğrudan yeniden çizemezsiniz ( `item-1` adresinin yenid Yukarıdaki örnekte, bir AJAX isteği için `$list` dizisine yalnızca bir öğe ekleneceğinden emin olmanız gerekir, bu nedenle `foreach` döngüsü yalnızca bir dinamik parçacık yazdıracaktır. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * This method returns data for the list. diff --git a/application/tr/creating-links.texy b/application/tr/creating-links.texy index 0cad61b67b..1b8325a446 100644 --- a/application/tr/creating-links.texy +++ b/application/tr/creating-links.texy @@ -52,7 +52,7 @@ Parametreler bir dizide saklanıyorsa, `...` operatörü (veya Latte 2.x'te `(ex Öznitelik `n:href` HTML etiketleri için çok kullanışlıdır ``. Bağlantıyı başka bir yere, örneğin metnin içine yazdırmak istiyorsak `{link}` adresini kullanırız: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` @@ -88,19 +88,19 @@ Bu format tüm Latte etiketleri ve bağlantılarla çalışan tüm sunum yöntem Bu nedenle temel form `Presenter:action` şeklindedir: ```latte -homepage +home ``` Geçerli sunucunun eylemine bağlantı verirsek, adını atlayabiliriz: ```latte -homepage +home ``` Eylem `default` ise, bunu atlayabiliriz, ancak iki nokta üst üste kalmalıdır: ```latte -homepage +home ``` Bağlantılar diğer [modüllere |modules] de işaret edebilir. Burada, bağlantılar alt modüllere göreli veya mutlak olarak ayırt edilir. Prensip disk yollarına benzer, sadece eğik çizgiler yerine iki nokta üst üste vardır. Gerçek sunucunun `Front` modülünün bir parçası olduğunu varsayalım, o zaman yazacağız: @@ -119,7 +119,7 @@ Bağlantılar diğer [modüllere |modules] de işaret edebilir. Burada, bağlant HTML sayfasının belirli bir bölümüne `#` hash sembolünden sonra gelen fragment adı verilen bir parça aracılığıyla bağlantı verebiliriz: ```latte -link to Homepage:default and fragment #main +link to Home:default and fragment #main ``` @@ -128,7 +128,7 @@ Mutlak Yollar .[#toc-absolute-paths] `link()` veya `n:href` tarafından oluşturulan bağlantılar her zaman mutlak yollardır (yani `/` ile başlarlar), ancak `https://domain` gibi bir protokol ve etki alanı içeren mutlak URL'ler değildir. -Mutlak bir URL oluşturmak için başına iki eğik çizgi ekleyin (örneğin, `n:href="//Homepage:"`). Ya da `$this->absoluteUrls = true` adresini ayarlayarak sunucuyu yalnızca mutlak bağlantılar oluşturacak şekilde değiştirebilirsiniz. +Mutlak bir URL oluşturmak için başına iki eğik çizgi ekleyin (örneğin, `n:href="//Home:"`). Ya da `$this->absoluteUrls = true` adresini ayarlayarak sunucuyu yalnızca mutlak bağlantılar oluşturacak şekilde değiştirebilirsiniz. Güncel Sayfaya Bağlantı .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ Bileşendeki Bağlantılar .[#toc-links-in-component] Bileşen şablonunda sunum yapanlara bağlantı vermek istiyorsak `{plink}` etiketini kullanırız: ```latte -homepage +home ``` veya kodda ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/tr/how-it-works.texy b/application/tr/how-it-works.texy index c3449f2c31..35bbb16a50 100644 --- a/application/tr/how-it-works.texy +++ b/application/tr/how-it-works.texy @@ -23,10 +23,10 @@ Dizin yapısı şuna benzer: web-project/ ├── app/ ← directory with application │ ├── Presenters/ ← presenter classes -│ │ ├── HomepagePresenter.php ← Homepage presenter class +│ │ ├── HomePresenter.php ← Home presenter class │ │ └── templates/ ← templates directory │ │ ├── @layout.latte ← template of shared layout -│ │ └── Homepage/ ← templates for Homepage presenter +│ │ └── Home/ ← templates for Home presenter │ │ └── default.latte ← template for action `default` │ ├── Router/ ← configuration of URL addresses │ └── Bootstrap.php ← booting class Bootstrap @@ -134,10 +134,10 @@ Sadece emin olmak için, tüm süreci biraz farklı bir URL ile özetlemeye çal 1) URL şu şekilde olacaktır `https://example.com` 2) uygulamayı önyüklüyoruz, bir konteyner oluşturuyoruz ve `Application::run()` -3) yönlendirici URL'yi bir çift olarak çözer `Homepage:default` -4) bir `HomepagePresenter` nesnesi oluşturulur +3) yönlendirici URL'yi bir çift olarak çözer `Home:default` +4) bir `HomePresenter` nesnesi oluşturulur 5) `renderDefault()` yöntemi çağrılır (eğer varsa) -6) `templates/@layout.latte` düzenine sahip bir `templates/Homepage/default.latte` şablonu oluşturulur +6) `templates/@layout.latte` düzenine sahip bir `templates/Home/default.latte` şablonu oluşturulur Şu anda birçok yeni kavramla karşılaşmış olabilirsiniz, ancak bunların anlamlı olduğuna inanıyoruz. Nette'de uygulama oluşturmak çocuk oyuncağı. diff --git a/application/tr/modules.texy b/application/tr/modules.texy index 2915afa552..e447d581bd 100644 --- a/application/tr/modules.texy +++ b/application/tr/modules.texy @@ -104,7 +104,7 @@ Haritalama .[#toc-mapping] Sınıf adının sunum yapan kişinin adından türetildiği kuralları tanımlar. Bunları [yapılandırmada |configuration] `application › mapping` anahtarının altına yazıyoruz. -Modül kullanmayan bir örnekle başlayalım. Sadece sunum yapan sınıfların `App\Presenters` ad alanına sahip olmasını isteyeceğiz. Bu, `Homepage` gibi bir sunucunun `App\Presenters\HomepagePresenter` sınıfıyla eşleşmesi gerektiği anlamına gelir. Bu, aşağıdaki yapılandırma ile gerçekleştirilebilir: +Modül kullanmayan bir örnekle başlayalım. Sadece sunum yapan sınıfların `App\Presenters` ad alanına sahip olmasını isteyeceğiz. Bu, `Home` gibi bir sunucunun `App\Presenters\HomePresenter` sınıfıyla eşleşmesi gerektiği anlamına gelir. Bu, aşağıdaki yapılandırma ile gerçekleştirilebilir: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Şimdi `Front:Homepage` sunucusu `App\Modules\Front\Presenters\HomepagePresenter` sınıfıyla ve `Admin:Dashboard` sunucusu `App\Modules\Admin\Presenters\DashboardPresenter` sınıfıyla eşleşir. +Şimdi `Front:Home` sunucusu `App\Modules\Front\Presenters\HomePresenter` sınıfıyla ve `Admin:Dashboard` sunucusu `App\Modules\Admin\Presenters\DashboardPresenter` sınıfıyla eşleşir. İlk ikisini değiştirmek için genel bir (yıldız) kural oluşturmak daha pratiktir. Ekstra yıldız işareti sadece modül için sınıf maskesine eklenecektir: diff --git a/application/tr/routing.texy b/application/tr/routing.texy index 5a77af89c1..b1812614ab 100644 --- a/application/tr/routing.texy +++ b/application/tr/routing.texy @@ -93,12 +93,12 @@ Rota şimdi `https://any-domain.com/chronicle/` parametresiyle birlikte `History Elbette, sunum yapan kişinin adı ve eylem de bir parametre olabilir. Örneğin: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Bu rota, örneğin `/article/edit` veya `/catalog/list` şeklinde bir URL'yi kabul eder ve bunları `Article:edit` veya `Catalog:list` sunucularına ve eylemlerine çevirir. -Ayrıca `presenter` ve `action` parametrelerine`Homepage` ve `default` varsayılan değerlerini verir ve bu nedenle bunlar isteğe bağlıdır. Böylece rota `/article` URL'sini de kabul eder ve bunu `Article:default` olarak çevirir. Ya da tam tersi, `Product:default` adresine bir bağlantı `/product` yolunu oluşturur, varsayılan `Homepage:default` adresine bir bağlantı `/` yolunu oluşturur. +Ayrıca `presenter` ve `action` parametrelerine`Home` ve `default` varsayılan değerlerini verir ve bu nedenle bunlar isteğe bağlıdır. Böylece rota `/article` URL'sini de kabul eder ve bunu `Article:default` olarak çevirir. Ya da tam tersi, `Product:default` adresine bir bağlantı `/product` yolunu oluşturur, varsayılan `Home:default` adresine bir bağlantı `/` yolunu oluşturur. Maske yalnızca site köküne dayalı göreli yolu değil, aynı zamanda eğik çizgi ile başladığında mutlak yolu, hatta iki eğik çizgi ile başladığında tüm mutlak URL'yi de tanımlayabilir: @@ -160,7 +160,7 @@ Diziler serbestçe iç içe geçirilebilir ve birleştirilebilir: ```php $router->addRoute( '[[-]/][/page-]', - 'Homepage:default', + 'Home:default', ); // Kabul edilen URL'ler: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Köşeli parantezler olmadan isteğe bağlı parametreler (yani varsayılan değere sahip parametreler) bu şekilde sarılmış gibi davranır: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // eşittir: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -En sağdaki eğik çizginin nasıl oluşturulduğunu değiştirmek için, yani `/homepage/` yerine bir `/homepage` almak için, rotayı bu şekilde ayarlayın: +En sağdaki eğik çizginin nasıl oluşturulduğunu değiştirmek için, yani `/home/` yerine bir `/home` almak için, rotayı bu şekilde ayarlayın: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ Genellikle `Presenter:action` biçiminde yazdığımız rotanın ikinci parametr ```php $router->addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ Filtreler ve Çeviriler .[#toc-filters-and-translations] Kaynak kodunu İngilizce yazmak iyi bir uygulamadır, ancak web sitenizin URL'sinin farklı bir dile çevrilmesi gerekiyorsa ne olacak? Gibi basit rotalar: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` `/product/123` veya `/cart` gibi İngilizce URL'ler oluşturacaktır. URL'deki sunucuların ve eylemlerin Almanca'ya çevrilmesini istiyorsak (örneğin `/produkt/123` veya `/einkaufswagen`), bir çeviri sözlüğü kullanabiliriz. Bunu eklemek için, ikinci parametrenin "daha konuşkan" bir varyantına zaten ihtiyacımız var: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // URL'deki dize => presenter 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ Belirli parametreler için filtrelerin yanı sıra, herhangi bir şekilde deği use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -504,14 +504,14 @@ http://example.com/?presenter=Product&action=detail&id=123 ```php // varsayılan olarak sunucu 'Anasayfa' ve eylem 'varsayılan' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` SimpleRouter'ı doğrudan [yapılandırmada |dependency-injection:services] tanımlamanızı öneririz: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ class MyRouter implements Nette\Routing\Router ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/tr/templates.texy b/application/tr/templates.texy index 6512641528..42aadf4b20 100644 --- a/application/tr/templates.texy +++ b/application/tr/templates.texy @@ -148,7 +148,7 @@ Bağlantı Oluşturma .[#toc-creating-links] Öznitelik `n:href` HTML etiketleri için çok kullanışlıdır ``. Bağlantıyı başka bir yere, örneğin metnin içine yazdırmak istiyorsak `{link}` adresini kullanırız: ```latte -URL is: {link Homepage:default} +URL is: {link Home:default} ``` Daha fazla bilgi için [Bağlantı Oluşturma |Creating Links] bölümüne bakın. diff --git a/application/uk/ajax.texy b/application/uk/ajax.texy index a05a0e96e6..a0f7c17300 100644 --- a/application/uk/ajax.texy +++ b/application/uk/ajax.texy @@ -149,7 +149,7 @@ $this->isControlInvalid('footer'); // -> true У наведеному прикладі необхідно переконатися, що під час AJAX-запиту до масиву `$list` буде додано тільки один елемент, тому цикл `foreach` виводитиме тільки один динамічний фрагмент. ```php -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { /** * Этот метод возвращает данные для списка. diff --git a/application/uk/creating-links.texy b/application/uk/creating-links.texy index 1ff9cc58b6..34ec0457fb 100644 --- a/application/uk/creating-links.texy +++ b/application/uk/creating-links.texy @@ -52,7 +52,7 @@ Атрибут `n:href` дуже зручний для HTML-тегів ``. Якщо ми хочемо вивести посилання в іншому місці, наприклад, у тексті, ми використовуємо `{link}`: ```latte -URL: {link Homepage:default} +URL: {link Home:default} ``` @@ -88,7 +88,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Тому основною формою є `Presenter:action`: ```latte -главная страница +главная страница ``` Якщо ми посилаємося на дію поточного презентера, ми можемо опустити його ім'я: @@ -100,7 +100,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Якщо дія є `default`, ми можемо опустити її, але двокрапка має залишитися: ```latte -главная страница +главная страница ``` Посилання можуть також вказувати на інші [модулі |modules]. Тут посилання розрізняються на відносні по відношенню до підмодулів або абсолютні. Принцип аналогічний дисковим шляхам, тільки замість косих рисок стоять двокрапки. Припустимо, що справжній презентер є частиною модуля `Front`, тоді ми напишемо: @@ -119,7 +119,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Ми можемо посилатися на певну частину HTML-сторінки через так званий фрагмент після хеш-символу `#`: ```latte -ссылка на Homepage:default и фрагмент #main +ссылка на Home:default и фрагмент #main ``` @@ -128,7 +128,7 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Посилання, що генеруються `link()` або `n:href`, завжди є абсолютними шляхами (тобто починаються з `/`), але не абсолютними URL з протоколом і доменом, як `https://domain`. -Щоб створити абсолютний URL, додайте дві косі риски на початок (наприклад, `n:href="//Homepage:"`). Або ви можете перемкнути презентатор на генерацію тільки абсолютних посилань, встановивши `$this->absoluteUrls = true`. +Щоб створити абсолютний URL, додайте дві косі риски на початок (наприклад, `n:href="//Home:"`). Або ви можете перемкнути презентатор на генерацію тільки абсолютних посилань, встановивши `$this->absoluteUrls = true`. Посилання на поточну сторінку .[#toc-link-to-current-page] @@ -213,13 +213,13 @@ $url = $this->link('Product:show', [$product->id, 'lang' => 'cs']); Якщо ми хочемо зробити посилання на презентери в шаблоні компонента, ми використовуємо тег `{plink}`: ```latte -главная страница +главная страница ``` or in the code ```php -$this->getPresenter()->link('Homepage:default') +$this->getPresenter()->link('Home:default') ``` diff --git a/application/uk/how-it-works.texy b/application/uk/how-it-works.texy index 548d29a4c2..903ddc003d 100644 --- a/application/uk/how-it-works.texy +++ b/application/uk/how-it-works.texy @@ -23,10 +23,10 @@ web-project/ ├── app/ ← каталог с приложением │ ├── Presenters/ ← классы презентеров -│ │ ├── HomepagePresenter.php ← Класс презентера главной страницы +│ │ ├── HomePresenter.php ← Класс презентера главной страницы │ │ └── templates/ ← директория шаблонов │ │ ├── @layout.latte ← шаблон общего макета -│ │ └── Homepage/ ← шаблоны презентера главной страницы +│ │ └── Home/ ← шаблоны презентера главной страницы │ │ └── default.latte ← шаблон действия `default` │ ├── Router/ ← конфигурация URL-адресов │ └── Bootstrap.php ← загрузочный класс Bootstrap @@ -134,10 +134,10 @@ class ProductPresenter extends Nette\Application\UI\Presenter 1) URL буде `https://example.com` 2) ми завантажуємо додаток, створюємо контейнер і запускаємо `Application::run()` -3) маршрутизатор декодує URL як пару `Homepage:default` -4) створюється об'єкт `HomepagePresenter` +3) маршрутизатор декодує URL як пару `Home:default` +4) створюється об'єкт `HomePresenter` 5) викликається метод `renderDefault()` (якщо існує) -6) шаблон `templates/Homepage/default.latte` з макетом `templates/@layout.latte` відмальований +6) шаблон `templates/Home/default.latte` з макетом `templates/@layout.latte` відмальований Можливо, зараз ви зіткнулися з безліччю нових понять, але ми вважаємо, що вони мають сенс. Створювати додатки в Nette - простіше простого. diff --git a/application/uk/modules.texy b/application/uk/modules.texy index 028dd4666b..5503142028 100644 --- a/application/uk/modules.texy +++ b/application/uk/modules.texy @@ -104,7 +104,7 @@ class DashboardPresenter extends Nette\Application\UI\Presenter Визначає правила, за якими ім'я класу виводиться з імені ведучого. Ми записуємо їх у [конфігурацію |configuration] під ключем `application › mapping`. -Почнемо з прикладу, в якому не використовуються модулі. Ми просто хочемо, щоб класи ведучого мали простір імен `App\Presenters`. Тобто ми хочемо, щоб ведучий, наприклад, `Homepage` відображався на клас `App\Presenters\HomepagePresenter`. Цього можна досягти за допомогою такої конфігурації: +Почнемо з прикладу, в якому не використовуються модулі. Ми просто хочемо, щоб класи ведучого мали простір імен `App\Presenters`. Тобто ми хочемо, щоб ведучий, наприклад, `Home` відображався на клас `App\Presenters\HomePresenter`. Цього можна досягти за допомогою такої конфігурації: ```neon application: @@ -124,7 +124,7 @@ application: Api: App\Api\*Presenter ``` -Тепер презентер `Front:Homepage` визначається класом `App\Modules\Front\HomepagePresenter`, а презентер `Admin:Dashboard` ` - `App\AdminModule\DashboardPresenter`. +Тепер презентер `Front:Home` визначається класом `App\Modules\Front\HomePresenter`, а презентер `Admin:Dashboard` ` - `App\AdminModule\DashboardPresenter`. Зручніше буде створити загальне правило (зірочка), яке замінить перші два правила і додасть додаткову зірочку тільки для модуля: diff --git a/application/uk/routing.texy b/application/uk/routing.texy index 0833ee1c7e..6295ad4828 100644 --- a/application/uk/routing.texy +++ b/application/uk/routing.texy @@ -93,12 +93,12 @@ $router->addRoute('chronicle/', 'History:show'); Звісно, ім'я презентера та дія також можуть бути параметрами. Наприклад: ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` Цей маршрут приймає, наприклад, URL у формі `/article/edit` і `/catalog/list` відповідно, і переводить їх у презентери та дії `Article:edit` і `Catalog:list` відповідно. -Він також надає параметрам `presenter` і `action` значення за замовчуванням `Homepage` і `default` і тому вони є необов'язковими. Тому маршрут також приймає URL `/article` і переводить його як `Article:default`. Або навпаки, посилання на `Product:default` генерує шлях `/product`, посилання на стандартну `Homepage:default` генерує шлях `/`. +Він також надає параметрам `presenter` і `action` значення за замовчуванням `Home` і `default` і тому вони є необов'язковими. Тому маршрут також приймає URL `/article` і переводить його як `Article:default`. Або навпаки, посилання на `Product:default` генерує шлях `/product`, посилання на стандартну `Home:default` генерує шлях `/`. Маска може описувати не тільки відносний шлях, що базується на корені сайту, а й абсолютний шлях, якщо він починається зі слеша, або навіть увесь абсолютний URL, якщо він починається з двох слешів: @@ -160,7 +160,7 @@ $router->addRoute('//[.]example.com//', /* ... */); ```php $router->addRoute( '[[-]/][page-]', - 'Homepage:default', + 'Home:default', ); // URL-адреси, що приймаються: @@ -183,16 +183,16 @@ $router->addRoute('[!.html]', /* ... */); Необов'язкові параметри (тобто параметри, що мають значення за замовчуванням) без квадратних дужок поводяться так, як якщо б вони були обгорнуті таким чином: ```php -$router->addRoute('//', /* ... */); +$router->addRoute('//', /* ... */); // дорівнює: -$router->addRoute('[/[/[]]]', /* ... */); +$router->addRoute('[/[/[]]]', /* ... */); ``` -Щоб змінити спосіб генерації самої правої косої риски, тобто замість `/homepage/` отримати `/homepage`, налаштуйте маршрут таким чином: +Щоб змінити спосіб генерації самої правої косої риски, тобто замість `/home/` отримати `/home`, налаштуйте маршрут таким чином: ```php -$router->addRoute('[[/[/]]]', /* ... */); +$router->addRoute('[[/[/]]]', /* ... */); ``` @@ -220,7 +220,7 @@ $router->addRoute('//www.%sld%.%tld%/%basePath%//addRoute('/[/]', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', ]); ``` @@ -232,7 +232,7 @@ use Nette\Routing\Route; $router->addRoute('/[/]', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', ], 'action' => [ Route::Value => 'default', @@ -252,7 +252,7 @@ $router->addRoute('/[/]', [ Доброю практикою є написання вихідного коду англійською мовою, але що якщо вам потрібно, щоб URL вашого сайту було перекладено іншою мовою? ```php -$router->addRoute('/', 'Homepage:default'); +$router->addRoute('/', 'Home:default'); ``` буде генерувати англійські URL, такі як `/product/123` або `/cart`. Якщо ми хочемо, щоб презентери та дії в URL були перекладені німецькою мовою (наприклад, `/produkt/123` або `/einkaufswagen`), ми можемо використовувати словник перекладів. Щоб додати його, нам уже потрібен "більш зрозумілий" варіант другого параметра: @@ -262,7 +262,7 @@ use Nette\Routing\Route; $router->addRoute('/', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterTable => [ // строка в URL => ведущий 'produkt' => 'Product', @@ -290,7 +290,7 @@ use Nette\Routing\Route; $router->addRoute('//', [ 'presenter' => [ - Route::Value => 'Homepage', + Route::Value => 'Home', Route::FilterIn => function (string $s): string { /* ... */ }, Route::FilterOut => function (string $s): string { /* ... */ }, ], @@ -313,7 +313,7 @@ $router->addRoute('//', [ use Nette\Routing\Route; $router->addRoute('/', [ - 'presenter' => 'Homepage', + 'presenter' => 'Home', 'action' => 'default', null => [ Route::FilterIn => function (array $params): array { /* ... */ }, @@ -503,15 +503,15 @@ http://example.com/?presenter=Product&action=detail&id=123 Параметром конструктора `SimpleRouter` є презентер і дія за замовчуванням, тобто дія, яку буде виконано, якщо ми відкриємо, наприклад, `http://example.com/` без додаткових параметрів. ```php -// використовуємо презентер 'Homepage' і дію 'default' -$router = new Nette\Application\Routers\SimpleRouter('Homepage:default'); +// використовуємо презентер 'Home' і дію 'default' +$router = new Nette\Application\Routers\SimpleRouter('Home:default'); ``` Ми рекомендуємо визначати SimpleRouter безпосередньо в [конфігурації |dependency-injection:services]: ```neon services: - - Nette\Application\Routers\SimpleRouter('Homepage:default') + - Nette\Application\Routers\SimpleRouter('Home:default') ``` @@ -611,7 +611,7 @@ class MyRouter implements Nette\Routing\Router ```php [ - 'presenter' => 'Front:Homepage', + 'presenter' => 'Front:Home', 'action' => 'default', ] ``` diff --git a/application/uk/templates.texy b/application/uk/templates.texy index b24a5fdb6a..6190a04b3f 100644 --- a/application/uk/templates.texy +++ b/application/uk/templates.texy @@ -148,7 +148,7 @@ public function renderDefault(): void Атрибут `n:href` дуже зручний для HTML-тегів. ``. Якщо ми хочемо вказати посилання в іншому місці, наприклад, у тексті, ми використовуємо `{link}`: ```latte -Adresa je: {link Homepage:default} +Adresa je: {link Home:default} ``` Додаткові відомості див. у розділі [Створення посилань URL |creating-links]. diff --git a/best-practices/bg/pagination.texy b/best-practices/bg/pagination.texy index 0ce839379c..be7883dffc 100644 --- a/best-practices/bg/pagination.texy +++ b/best-practices/bg/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Следващата стъпка е да редактирате водещия. Ще предадем номера на текущо показваната страница на метода `render`. В случай че този номер не е част от URL адреса, трябва да зададем стойност по подразбиране за първата страница. -Също така разширяваме метода `render`, за да получим инстанцията Paginator, да я конфигурираме и да изберем желаните статии, които да се показват в шаблона. HomepagePresenter ще изглежда по следния начин: +Също така разширяваме метода `render`, за да получим инстанцията Paginator, да я конфигурираме и да изберем желаните статии, които да се показват в шаблона. HomePresenter ще изглежда по следния начин: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/cs/pagination.texy b/best-practices/cs/pagination.texy index b93dda4860..46a00cd256 100644 --- a/best-practices/cs/pagination.texy +++ b/best-practices/cs/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Následně se pustíme do úprav presenteru. Do render metody budeme předávat číslo aktuálně zobrazené stránky. Pro případ, kdy nebude toto číslo součástí URL, nastavíme výchozí hodnotu první stránky. -Dále také render metodu rozšíříme o získání instance Paginatoru, jeho nastavení a výběru správných článků pro zobrazení v šabloně. HomepagePresenter bude po úpravách vypadat takto: +Dále také render metodu rozšíříme o získání instance Paginatoru, jeho nastavení a výběru správných článků pro zobrazení v šabloně. HomePresenter bude po úpravách vypadat takto: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/de/pagination.texy b/best-practices/de/pagination.texy index 7ab9e78376..5775e52df1 100644 --- a/best-practices/de/pagination.texy +++ b/best-practices/de/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Der nächste Schritt besteht darin, den Präsentator zu bearbeiten. Wir werden die Nummer der aktuell angezeigten Seite an die Render-Methode weiterleiten. Für den Fall, dass diese Nummer nicht Teil der URL ist, müssen wir den Standardwert auf die erste Seite setzen. -Wir erweitern die Render-Methode auch, um die Paginator-Instanz zu erhalten, sie einzurichten und die richtigen Artikel für die Anzeige in der Vorlage auszuwählen. Der HomepagePresenter wird wie folgt aussehen: +Wir erweitern die Render-Methode auch, um die Paginator-Instanz zu erhalten, sie einzurichten und die richtigen Artikel für die Anzeige in der Vorlage auszuwählen. Der HomePresenter wird wie folgt aussehen: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/el/pagination.texy b/best-practices/el/pagination.texy index 3d509a8fe4..3003b38a84 100644 --- a/best-practices/el/pagination.texy +++ b/best-practices/el/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Το επόμενο βήμα είναι να επεξεργαστούμε τον παρουσιαστή. Θα προωθήσουμε τον αριθμό της τρέχουσας σελίδας που εμφανίζεται στη μέθοδο render. Στην περίπτωση που αυτός ο αριθμός δεν αποτελεί μέρος της διεύθυνσης URL, πρέπει να ορίσουμε την προεπιλεγμένη τιμή στην πρώτη σελίδα. -Επεκτείνουμε επίσης τη μέθοδο render για να λάβουμε την περίπτωση Paginator, να τη ρυθμίσουμε και να επιλέξουμε τα σωστά άρθρα που θα εμφανίζονται στο πρότυπο. Το HomepagePresenter θα μοιάζει με αυτό: +Επεκτείνουμε επίσης τη μέθοδο render για να λάβουμε την περίπτωση Paginator, να τη ρυθμίσουμε και να επιλέξουμε τα σωστά άρθρα που θα εμφανίζονται στο πρότυπο. Το HomePresenter θα μοιάζει με αυτό: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/en/pagination.texy b/best-practices/en/pagination.texy index 107ea8f3cd..c0c0007e9b 100644 --- a/best-practices/en/pagination.texy +++ b/best-practices/en/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository The next step is to edit the presenter. We will forward the number of the currently displayed page to the render method. In the case that this number is not part of the URL, we need to set the default value to the first page. -We also expand the render method to get the Paginator instance, setting it up, and selecting the correct articles to display in the template. HomepagePresenter will look like this: +We also expand the render method to get the Paginator instance, setting it up, and selecting the correct articles to display in the template. HomePresenter will look like this: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/es/pagination.texy b/best-practices/es/pagination.texy index 36b9ee472b..4638f53da7 100644 --- a/best-practices/es/pagination.texy +++ b/best-practices/es/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository El siguiente paso es editar el presentador. Enviaremos el número de la página actualmente mostrada al método render. En el caso de que este número no forme parte de la URL, debemos establecer el valor por defecto en la primera página. -También expandimos el método render para obtener la instancia Paginator, configurándola, y seleccionando los artículos correctos para mostrar en la plantilla. HomepagePresenter tendrá este aspecto: +También expandimos el método render para obtener la instancia Paginator, configurándola, y seleccionando los artículos correctos para mostrar en la plantilla. HomePresenter tendrá este aspecto: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/fr/pagination.texy b/best-practices/fr/pagination.texy index 56b6d0a74a..9cb7e34af0 100644 --- a/best-practices/fr/pagination.texy +++ b/best-practices/fr/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository L'étape suivante consiste à modifier le présentateur. Nous allons transmettre le numéro de la page actuellement affichée à la méthode de rendu. Dans le cas où ce numéro ne fait pas partie de l'URL, nous devons définir la valeur par défaut sur la première page. -Nous étendons également la méthode de rendu pour obtenir l'instance de Paginator, la configurer et sélectionner les bons articles à afficher dans le modèle. Le HomepagePresenter ressemblera à ceci : +Nous étendons également la méthode de rendu pour obtenir l'instance de Paginator, la configurer et sélectionner les bons articles à afficher dans le modèle. Le HomePresenter ressemblera à ceci : ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/hu/pagination.texy b/best-practices/hu/pagination.texy index cb1e94513f..eee3c4024b 100644 --- a/best-practices/hu/pagination.texy +++ b/best-practices/hu/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository A következő lépés a bemutató szerkesztése. Az aktuálisan megjelenített oldal számát továbbítjuk a render metódusnak. Abban az esetben, ha ez a szám nem része az URL-nek, akkor az alapértelmezett értéket az első oldalra kell beállítanunk. -A render metódust kibővítjük a Paginator példány megszerzésével, beállításával és a sablonban megjelenítendő megfelelő cikkek kiválasztásával is. A HomepagePresenter így fog kinézni: +A render metódust kibővítjük a Paginator példány megszerzésével, beállításával és a sablonban megjelenítendő megfelelő cikkek kiválasztásával is. A HomePresenter így fog kinézni: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/it/pagination.texy b/best-practices/it/pagination.texy index a046778ed4..f88acbacb5 100644 --- a/best-practices/it/pagination.texy +++ b/best-practices/it/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Il passo successivo è modificare il presentatore. Inoltreremo il numero della pagina attualmente visualizzata al metodo render. Nel caso in cui questo numero non faccia parte dell'URL, occorre impostare il valore predefinito alla prima pagina. -Espandiamo inoltre il metodo render per ottenere l'istanza di Paginator, impostandola e selezionando gli articoli corretti da visualizzare nel template. HomepagePresenter avrà questo aspetto: +Espandiamo inoltre il metodo render per ottenere l'istanza di Paginator, impostandola e selezionando gli articoli corretti da visualizzare nel template. HomePresenter avrà questo aspetto: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/pl/pagination.texy b/best-practices/pl/pagination.texy index 58ea4aa173..83ff2c4cd9 100644 --- a/best-practices/pl/pagination.texy +++ b/best-practices/pl/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Następnie zabierzemy się do pracy nad modyfikacją prezentera. Do metody render przekażemy numer aktualnie wyświetlanej strony. W przypadku, gdy ten numer nie jest częścią adresu URL, ustawimy domyślną wartość pierwszej strony. -Następnie rozszerzymy również metodę render, aby uzyskać instancję Paginatora, skonfigurować ją i wybrać odpowiednie artykuły do wyświetlenia w szablonie. HomepagePresenter po modyfikacjach będzie wyglądał tak: +Następnie rozszerzymy również metodę render, aby uzyskać instancję Paginatora, skonfigurować ją i wybrać odpowiednie artykuły do wyświetlenia w szablonie. HomePresenter po modyfikacjach będzie wyglądał tak: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/pt/pagination.texy b/best-practices/pt/pagination.texy index a57ad3f8ed..dfce31dfc6 100644 --- a/best-practices/pt/pagination.texy +++ b/best-practices/pt/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/ro/pagination.texy b/best-practices/ro/pagination.texy index 1a87368cee..4175e89bd9 100644 --- a/best-practices/ro/pagination.texy +++ b/best-practices/ro/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Următorul pas este să modificăm prezentatorul. Vom transmite numărul paginii afișate în prezent către metoda de randare. În cazul în care acest număr nu face parte din URL, trebuie să stabilim valoarea implicită la prima pagină. -De asemenea, extindem metoda de randare pentru a obține instanța Paginator, configurând-o și selectând articolele corecte pentru a fi afișate în șablon. HomepagePresenter va arăta astfel: +De asemenea, extindem metoda de randare pentru a obține instanța Paginator, configurând-o și selectând articolele corecte pentru a fi afișate în șablon. HomePresenter va arăta astfel: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/ru/pagination.texy b/best-practices/ru/pagination.texy index 5907e5cf1c..11f52f40f2 100644 --- a/best-practices/ru/pagination.texy +++ b/best-practices/ru/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Следующим шагом будет редактирование презентера. Мы передадим номер текущей отображаемой страницы в метод `render`. В случае, если этот номер не является частью URL, нам нужно установить значение по умолчанию для первой страницы. -Мы также расширяем метод `render` для получения экземпляра Paginator, его настройки и выбора нужных статей для отображения в шаблоне. HomepagePresenter будет выглядеть следующим образом: +Мы также расширяем метод `render` для получения экземпляра Paginator, его настройки и выбора нужных статей для отображения в шаблоне. HomePresenter будет выглядеть следующим образом: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/sl/pagination.texy b/best-practices/sl/pagination.texy index 208bf2d409..b5322e1cd1 100644 --- a/best-practices/sl/pagination.texy +++ b/best-practices/sl/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Naslednji korak je urejanje predstavnika. Številko trenutno prikazane strani bomo posredovali metodi render. V primeru, da ta številka ni del naslova URL, moramo privzeto vrednost nastaviti na prvo stran. -Metodo upodabljanja razširimo tudi na pridobitev primerka Paginatorja, njegovo nastavitev in izbiro pravilnih člankov za prikaz v predlogi. HomepagePresenter bo videti takole: +Metodo upodabljanja razširimo tudi na pridobitev primerka Paginatorja, njegovo nastavitev in izbiro pravilnih člankov za prikaz v predlogi. HomePresenter bo videti takole: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/tr/pagination.texy b/best-practices/tr/pagination.texy index 6dc3b85abb..5ae4641731 100644 --- a/best-practices/tr/pagination.texy +++ b/best-practices/tr/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Bir sonraki adım sunucuyu düzenlemektir. Şu anda görüntülenen sayfanın numarasını render yöntemine ileteceğiz. Bu numaranın URL'nin bir parçası olmaması durumunda, varsayılan değeri ilk sayfaya ayarlamamız gerekir. -Ayrıca Paginator örneğini almak, ayarlamak ve şablonda görüntülenecek doğru makaleleri seçmek için render yöntemini genişletiyoruz. HomepagePresenter şu şekilde görünecektir: +Ayrıca Paginator örneğini almak, ayarlamak ve şablonda görüntülenecek doğru makaleleri seçmek için render yöntemini genişletiyoruz. HomePresenter şu şekilde görünecektir: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/best-practices/uk/pagination.texy b/best-practices/uk/pagination.texy index 5c2d9cef64..b0883298b5 100644 --- a/best-practices/uk/pagination.texy +++ b/best-practices/uk/pagination.texy @@ -39,7 +39,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -111,7 +111,7 @@ class ArticleRepository Наступним кроком буде редагування презентера. Ми передамо номер поточної відображуваної сторінки в метод `render`. У разі, якщо цей номер не є частиною URL, нам потрібно встановити значення за замовчуванням для першої сторінки. -Ми також розширюємо метод `render` для отримання екземпляра Paginator, його налаштування та вибору потрібних статей для відображення в шаблоні. HomepagePresenter матиме такий вигляд: +Ми також розширюємо метод `render` для отримання екземпляра Paginator, його налаштування та вибору потрібних статей для відображення в шаблоні. HomePresenter матиме такий вигляд: ```php namespace App\Presenters; @@ -119,7 +119,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, @@ -216,7 +216,7 @@ namespace App\Presenters; use Nette; use App\Model\ArticleRepository; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { public function __construct( private ArticleRepository $articleRepository, diff --git a/forms/bg/in-presenter.texy b/forms/bg/in-presenter.texy index 8eaf24ef10..ae2285540f 100644 --- a/forms/bg/in-presenter.texy +++ b/forms/bg/in-presenter.texy @@ -30,11 +30,11 @@ $form->onSuccess[] = [$this, 'formSucceeded']; От гледна точка на водещия формулярът е общ компонент. Затова той се третира като компонент и се включва в презентатора чрез [метода factory |application:components#Factory-Methods]. Това ще изглежда по следния начин: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name съдържа името // $data->password съдържа парола $this->flashMessage('Регистрирахте се успешно.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` А визуализирането в шаблона се извършва с помощта на тага `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Регистрация

{control registrationForm} diff --git a/forms/bg/validation.texy b/forms/bg/validation.texy index 3caa7d15f9..742c787c3c 100644 --- a/forms/bg/validation.texy +++ b/forms/bg/validation.texy @@ -234,7 +234,7 @@ public function validateSignInForm(Form $form, \stdClass $data): void try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/cs/in-presenter.texy b/forms/cs/in-presenter.texy index e4934e14b5..f3174f1c0a 100644 --- a/forms/cs/in-presenter.texy +++ b/forms/cs/in-presenter.texy @@ -30,11 +30,11 @@ Formulář v presenteru je objekt třídy `Nette\Application\UI\Form`, její př Z pohledu presenteru je formulář běžná komponenta. Proto se s ním jako s komponentou zachází a začleníme ji do presenteru pomocí [tovární metody |application:components#Tovární metody]. Bude to vypadat takto: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name obsahuje jméno // $data->password obsahuje heslo $this->flashMessage('Byl jste úspěšně registrován.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` A v šabloně formulář vykreslíme značkou `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registrace

{control registrationForm} diff --git a/forms/cs/validation.texy b/forms/cs/validation.texy index a2232fe491..b20f5f60fe 100644 --- a/forms/cs/validation.texy +++ b/forms/cs/validation.texy @@ -234,7 +234,7 @@ V mnoha případech se o chybě dozvíme až ve chvíli, kdy zpracováváme plat try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/de/in-presenter.texy b/forms/de/in-presenter.texy index 4373a2fdb6..41f8158caf 100644 --- a/forms/de/in-presenter.texy +++ b/forms/de/in-presenter.texy @@ -30,11 +30,11 @@ Das Formular im Presenter ist ein Objekt der Klasse `Nette\Application\UI\Form`, Aus der Sicht des Präsentators ist das Formular eine gemeinsame Komponente. Daher wird es als Komponente behandelt und mit der [Factory-Methode |application:components#Factory Methods] in den Presenter eingebunden. Das sieht dann wie folgt aus: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name enthält Name // $data->password enthält das Passwort $this->flashMessage('Sie haben sich erfolgreich angemeldet.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` Und das Rendern in der Vorlage erfolgt mit dem Tag `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/de/validation.texy b/forms/de/validation.texy index 3b35a02ae8..29dd24e760 100644 --- a/forms/de/validation.texy +++ b/forms/de/validation.texy @@ -234,7 +234,7 @@ In vielen Fällen entdecken wir einen Fehler, wenn wir ein gültiges Formular ve try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/el/in-presenter.texy b/forms/el/in-presenter.texy index 1e2a7a8105..9221cee979 100644 --- a/forms/el/in-presenter.texy +++ b/forms/el/in-presenter.texy @@ -30,11 +30,11 @@ $form->onSuccess[] = [$this, 'formSucceeded']; Από τη σκοπιά του παρουσιαστή, η φόρμα είναι ένα κοινό συστατικό. Ως εκ τούτου, αντιμετωπίζεται ως συστατικό και ενσωματώνεται στον παρουσιαστή χρησιμοποιώντας τη [μέθοδο factory |application:components#Factory Methods]. Αυτό θα έχει την εξής μορφή: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name περιέχει όνομα // $data->password περιέχει κωδικό πρόσβασης $this->flashMessage('You have successfully signed up.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` Και η απόδοση στο πρότυπο γίνεται με τη χρήση της ετικέτας `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/el/validation.texy b/forms/el/validation.texy index f619c8c2c0..0cee3fe0f0 100644 --- a/forms/el/validation.texy +++ b/forms/el/validation.texy @@ -234,7 +234,7 @@ public function validateSignInForm(Form $form, \stdClass $data): void try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/en/in-presenter.texy b/forms/en/in-presenter.texy index 831db327ce..e09ebee337 100644 --- a/forms/en/in-presenter.texy +++ b/forms/en/in-presenter.texy @@ -30,11 +30,11 @@ The form in the presenter is an object of the class `Nette\Application\UI\Form`, From the presenter's point of view, the form is a common component. Therefore, it is treated as a component and incorporated into the presenter using [factory method |application:components#Factory Methods]. It will look like this: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name contains name // $data->password contains password $this->flashMessage('You have successfully signed up.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` And render in template is done using `{control}` tag: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/en/validation.texy b/forms/en/validation.texy index 9d144b4c58..fd50eb7e4f 100644 --- a/forms/en/validation.texy +++ b/forms/en/validation.texy @@ -234,7 +234,7 @@ In many cases, we discover an error when we are processing a valid form, e.g. wh try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/es/in-presenter.texy b/forms/es/in-presenter.texy index 63a3ece9e5..2dddcee064 100644 --- a/forms/es/in-presenter.texy +++ b/forms/es/in-presenter.texy @@ -30,11 +30,11 @@ El formulario en el presentador es un objeto de la clase `Nette\Application\UI\F Desde el punto de vista del presentador, el formulario es un componente común. Por lo tanto, se trata como un componente y se incorpora al presentador utilizando [el método factory |application:components#Factory Methods]. Tendrá el siguiente aspecto: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name contiene nombre // $data->password contiene contraseña $this->flashMessage('You have successfully signed up.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` Y el renderizado en la plantilla se realiza utilizando la etiqueta `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registro

{control registrationForm} diff --git a/forms/es/validation.texy b/forms/es/validation.texy index 3ad0b76dd1..9b7e35bc31 100644 --- a/forms/es/validation.texy +++ b/forms/es/validation.texy @@ -234,7 +234,7 @@ En muchos casos, descubrimos un error cuando estamos procesando un formulario v try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/fr/in-presenter.texy b/forms/fr/in-presenter.texy index 2c44d71d95..0fb310f2cb 100644 --- a/forms/fr/in-presenter.texy +++ b/forms/fr/in-presenter.texy @@ -30,11 +30,11 @@ Le formulaire dans le présentateur est un objet de la classe `Nette\Application Du point de vue du présentateur, le formulaire est un composant commun. Par conséquent, il est traité comme un composant et incorporé dans le présentateur à l'aide de la [méthode factory |application:components#Factory Methods]. Cela ressemblera à ceci : -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name contient le nom // $data->password contient password $this->flashMessage('Vous vous êtes inscrit avec succès.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` Et le rendu dans le modèle est effectué à l'aide de la balise `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/fr/validation.texy b/forms/fr/validation.texy index 2d2623330d..361456d8c7 100644 --- a/forms/fr/validation.texy +++ b/forms/fr/validation.texy @@ -234,7 +234,7 @@ Dans de nombreux cas, nous découvrons une erreur lors du traitement d'un formul try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/hu/in-presenter.texy b/forms/hu/in-presenter.texy index f8771aca48..d3bc0884a4 100644 --- a/forms/hu/in-presenter.texy +++ b/forms/hu/in-presenter.texy @@ -30,11 +30,11 @@ A prezenterben lévő űrlap a `Nette\Application\UI\Form` osztály objektuma, e A bemutató szempontjából az űrlap egy közös komponens. Ezért komponensként kezeljük, és a [factory metódus |application:components#Factory Methods] segítségével beépítjük a prezentálóba. Ez így fog kinézni: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name tartalmazza a nevet // $data->password tartalmazza a jelszót $this->flashMessage('Sikeresen regisztrált.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` A sablonban történő megjelenítés pedig a `{control}` tag használatával történik: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/hu/validation.texy b/forms/hu/validation.texy index 7fd997f488..c75844fbdb 100644 --- a/forms/hu/validation.texy +++ b/forms/hu/validation.texy @@ -234,7 +234,7 @@ Sok esetben egy érvényes űrlap feldolgozása közben fedezünk fel hibát, p try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/it/in-presenter.texy b/forms/it/in-presenter.texy index 3b4c40bce0..a7b68bbb17 100644 --- a/forms/it/in-presenter.texy +++ b/forms/it/in-presenter.texy @@ -30,11 +30,11 @@ Il modulo nel presentatore è un oggetto della classe `Nette\Application\UI\Form Dal punto di vista del presentatore, il modulo è un componente comune. Pertanto, viene trattato come un componente e incorporato nel presentatore con il [metodo factory |application:components#Factory Methods]. L'aspetto sarà il seguente: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name contiene nome // $data->password contiene la password $this->flashMessage('Ti sei iscritto con successo.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` E il rendering nel template viene effettuato utilizzando il tag `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/it/validation.texy b/forms/it/validation.texy index 7f435fd398..1602645bce 100644 --- a/forms/it/validation.texy +++ b/forms/it/validation.texy @@ -234,7 +234,7 @@ In molti casi, si scopre un errore durante l'elaborazione di un modulo valido, a try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/pl/in-presenter.texy b/forms/pl/in-presenter.texy index f148d9e8b7..ff8db944ce 100644 --- a/forms/pl/in-presenter.texy +++ b/forms/pl/in-presenter.texy @@ -30,11 +30,11 @@ Formularz w prezenterze jest obiektem klasy `Nette\Application\UI\Form`, jego po Z punktu widzenia prezentera formularz jest normalnym komponentem. Dlatego traktujemy go jako komponent i włączamy do prezentera za pomocą [metody factory |application:components#Factory-Methods]. Będzie to wyglądało tak: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -59,7 +59,7 @@ class HomepagePresenter extends Nette\Application\UI\Presenter A w szablonie renderujemy formularz z tagiem `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registrace

{control registrationForm} diff --git a/forms/pt/in-presenter.texy b/forms/pt/in-presenter.texy index a79555276c..81cf7df2ae 100644 --- a/forms/pt/in-presenter.texy +++ b/forms/pt/in-presenter.texy @@ -30,11 +30,11 @@ A forma no apresentador é um objeto da classe `Nette\Application\UI\Form`, seu Do ponto de vista do apresentador, a forma é um componente comum. Portanto, ele é tratado como um componente e incorporado ao apresentador usando [o método de fábrica |application:components#Factory Methods]. Será parecido com isto: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name contém nome // $data->password contém a senha $this->flashMessage('Você se inscreveu com sucesso'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` E a renderização em modelo é feita usando a tag `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/pt/validation.texy b/forms/pt/validation.texy index 36a257c50a..fe1ab31f0d 100644 --- a/forms/pt/validation.texy +++ b/forms/pt/validation.texy @@ -234,7 +234,7 @@ Em muitos casos, descobrimos um erro quando estamos processando um formulário v try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/ro/in-presenter.texy b/forms/ro/in-presenter.texy index 9284a8e606..fb18bf4c66 100644 --- a/forms/ro/in-presenter.texy +++ b/forms/ro/in-presenter.texy @@ -30,11 +30,11 @@ Formularul din prezentator este un obiect din clasa `Nette\Application\UI\Form`, Din punctul de vedere al prezentatorului, formularul este o componentă comună. Prin urmare, acesta este tratat ca o componentă și încorporat în prezentator folosind [metoda factory |application:components#Factory Methods]. Acesta va arăta astfel: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name conține nume // $data->password conține parola $this->flashMessage('You have successfully signed up.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` Iar redarea în șablon se face cu ajutorul etichetei `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/ro/validation.texy b/forms/ro/validation.texy index 3aac33825d..87d26482db 100644 --- a/forms/ro/validation.texy +++ b/forms/ro/validation.texy @@ -234,7 +234,7 @@ Erori de procesare .[#toc-processing-errors] try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/ru/in-presenter.texy b/forms/ru/in-presenter.texy index 9fcc9be442..f8ba0bff66 100644 --- a/forms/ru/in-presenter.texy +++ b/forms/ru/in-presenter.texy @@ -30,11 +30,11 @@ $form->onSuccess[] = [$this, 'formSucceeded']; С точки зрения презентера форма является общим компонентом. Поэтому она рассматривается как компонент и включается в презентер с помощью [фабричного метода |application:components#Factory-Methods]. Это будет выглядеть следующим образом: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name содержит имя // $data->password содержит пароль $this->flashMessage('Вы успешно зарегистрировались.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` А рендеринг в шаблоне осуществляется с помощью тега `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Регистрация

{control registrationForm} diff --git a/forms/ru/validation.texy b/forms/ru/validation.texy index df6e71d5da..b3f60e1a21 100644 --- a/forms/ru/validation.texy +++ b/forms/ru/validation.texy @@ -234,7 +234,7 @@ public function validateSignInForm(Form $form, \stdClass $data): void try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/sl/in-presenter.texy b/forms/sl/in-presenter.texy index 576f38ebcd..bf919a5eb2 100644 --- a/forms/sl/in-presenter.texy +++ b/forms/sl/in-presenter.texy @@ -30,11 +30,11 @@ Obrazec v predstavitvi je objekt razreda `Nette\Application\UI\Form`, njegov pre Z vidika predstavnika je obrazec skupna komponenta. Zato ga obravnavamo kot komponento in ga vključimo v predstavitveni program z uporabo [tovarniške metode |application:components#Factory Methods]. To bo videti takole: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name vsebuje ime // $data->password vsebuje geslo $this->flashMessage('You have successfully signed up.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` Prikaz v predlogi pa se izvede z uporabo oznake `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/sl/validation.texy b/forms/sl/validation.texy index e2b1ce220b..a1f35438fb 100644 --- a/forms/sl/validation.texy +++ b/forms/sl/validation.texy @@ -234,7 +234,7 @@ V številnih primerih odkrijemo napako med obdelavo veljavnega obrazca, npr. ko try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/tr/in-presenter.texy b/forms/tr/in-presenter.texy index cb1e9c6ab2..cef4cc8ee8 100644 --- a/forms/tr/in-presenter.texy +++ b/forms/tr/in-presenter.texy @@ -30,11 +30,11 @@ Sunucudaki form `Nette\Application\UI\Form` sınıfının bir nesnesidir, selefi Sunucunun bakış açısından, form ortak bir bileşendir. Bu nedenle, bir bileşen olarak ele alınır ve [fabrika yöntemi |application:components#Factory Methods] kullanılarak sunucuya dahil edilir. Bu şekilde görünecektir: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name isim içeriyor // $data->password parola içerir $this->flashMessage('Başarıyla kaydoldunuz.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` Ve şablonda render işlemi `{control}` etiketi kullanılarak yapılır: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Registration

{control registrationForm} diff --git a/forms/tr/validation.texy b/forms/tr/validation.texy index bb45ad8d60..2d02efe8a3 100644 --- a/forms/tr/validation.texy +++ b/forms/tr/validation.texy @@ -234,7 +234,7 @@ public function validateSignInForm(Form $form, \stdClass $data): void try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/forms/uk/in-presenter.texy b/forms/uk/in-presenter.texy index cf22c6a937..ed89773929 100644 --- a/forms/uk/in-presenter.texy +++ b/forms/uk/in-presenter.texy @@ -30,11 +30,11 @@ $form->onSuccess[] = [$this, 'formSucceeded']; З точки зору презентера форма є загальним компонентом. Тому вона розглядається як компонент і включається в презентер за допомогою [фабричного методу |application:components#Factory-Methods]. Це виглядатиме наступним чином: -```php .{file:app/Presenters/HomepagePresenter.php} +```php .{file:app/Presenters/HomePresenter.php} use Nette; use Nette\Application\UI\Form; -class HomepagePresenter extends Nette\Application\UI\Presenter +class HomePresenter extends Nette\Application\UI\Presenter { protected function createComponentRegistrationForm(): Form { @@ -52,14 +52,14 @@ class HomepagePresenter extends Nette\Application\UI\Presenter // $data->name містить ім'я // $data->password містить пароль $this->flashMessage('Ви успішно зареєструвалися.'); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } } ``` А рендеринг у шаблоні здійснюється за допомогою тега `{control}`: -```latte .{file:app/Presenters/templates/Homepage/default.latte} +```latte .{file:app/Presenters/templates/Home/default.latte}

Регистрация

{control registrationForm} diff --git a/forms/uk/validation.texy b/forms/uk/validation.texy index 9d38011c8b..a9ca94d241 100644 --- a/forms/uk/validation.texy +++ b/forms/uk/validation.texy @@ -234,7 +234,7 @@ public function validateSignInForm(Form $form, \stdClass $data): void try { $data = $form->getValues(); $this->user->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) { diff --git a/latte/bg/template-inheritance.texy b/latte/bg/template-inheritance.texy index b35b1dfea1..921d9ad67e 100644 --- a/latte/bg/template-inheritance.texy +++ b/latte/bg/template-inheritance.texy @@ -603,7 +603,7 @@ Hi, I am Mary. ```latte ``` diff --git a/latte/cs/template-inheritance.texy b/latte/cs/template-inheritance.texy index 9f711b4c7d..92e78495b2 100644 --- a/latte/cs/template-inheritance.texy +++ b/latte/cs/template-inheritance.texy @@ -603,7 +603,7 @@ V Latte existují různé typy dědičnosti a opětovného použití kódu. Poj ```latte ``` diff --git a/latte/de/template-inheritance.texy b/latte/de/template-inheritance.texy index 8ef36e9e12..f92ff5f344 100644 --- a/latte/de/template-inheritance.texy +++ b/latte/de/template-inheritance.texy @@ -603,7 +603,7 @@ Es gibt verschiedene Arten der Vererbung und der Wiederverwendung von Code in La ```latte ``` diff --git a/latte/el/template-inheritance.texy b/latte/el/template-inheritance.texy index eece8ef222..a81e0fcbd5 100644 --- a/latte/el/template-inheritance.texy +++ b/latte/el/template-inheritance.texy @@ -603,7 +603,7 @@ Hi, I am Mary. ```latte ``` diff --git a/latte/en/template-inheritance.texy b/latte/en/template-inheritance.texy index 7a5559d0ee..7d3e10002b 100644 --- a/latte/en/template-inheritance.texy +++ b/latte/en/template-inheritance.texy @@ -603,7 +603,7 @@ There are various types of inheritance and code reuse in Latte. Let's summarize ```latte ``` diff --git a/latte/es/template-inheritance.texy b/latte/es/template-inheritance.texy index b36ab94df3..8ac35f7efb 100644 --- a/latte/es/template-inheritance.texy +++ b/latte/es/template-inheritance.texy @@ -603,7 +603,7 @@ Existen varios tipos de herencia y reutilización de código en Latte. Vamos a r ```latte ``` diff --git a/latte/fr/template-inheritance.texy b/latte/fr/template-inheritance.texy index 919bf91a70..871677ec23 100644 --- a/latte/fr/template-inheritance.texy +++ b/latte/fr/template-inheritance.texy @@ -603,7 +603,7 @@ Il existe différents types d'héritage et de réutilisation du code dans Latte. ```latte ``` diff --git a/latte/hu/template-inheritance.texy b/latte/hu/template-inheritance.texy index 7353e6e8a5..7ed8e1e0ec 100644 --- a/latte/hu/template-inheritance.texy +++ b/latte/hu/template-inheritance.texy @@ -603,7 +603,7 @@ A Latte-ban az öröklésnek és a kód újrafelhasználásának különböző t ```latte ``` diff --git a/latte/it/template-inheritance.texy b/latte/it/template-inheritance.texy index 77a692ff1e..10d70b4ad3 100644 --- a/latte/it/template-inheritance.texy +++ b/latte/it/template-inheritance.texy @@ -603,7 +603,7 @@ In Latte esistono vari tipi di ereditarietà e di riutilizzo del codice. Riassum ```latte ``` diff --git a/latte/ja/template-inheritance.texy b/latte/ja/template-inheritance.texy index 605c539d76..c94887932e 100644 --- a/latte/ja/template-inheritance.texy +++ b/latte/ja/template-inheritance.texy @@ -603,7 +603,7 @@ Latteには様々な種類の継承やコードの再利用があります。よ ```latte ``` diff --git a/latte/pl/template-inheritance.texy b/latte/pl/template-inheritance.texy index 680bc0ed23..375f7ddd72 100644 --- a/latte/pl/template-inheritance.texy +++ b/latte/pl/template-inheritance.texy @@ -603,7 +603,7 @@ W Latte istnieją różne rodzaje dziedziczenia i ponownego wykorzystania kodu. ```latte ``` diff --git a/latte/pt/template-inheritance.texy b/latte/pt/template-inheritance.texy index fc2c2f3f05..e9e4598a49 100644 --- a/latte/pt/template-inheritance.texy +++ b/latte/pt/template-inheritance.texy @@ -603,7 +603,7 @@ Há vários tipos de herança e reutilização de código em Latte. Vamos resumi ```latte ``` diff --git a/latte/ro/template-inheritance.texy b/latte/ro/template-inheritance.texy index 6a3e78166d..98cd9105bf 100644 --- a/latte/ro/template-inheritance.texy +++ b/latte/ro/template-inheritance.texy @@ -603,7 +603,7 @@ Există diferite tipuri de moștenire și reutilizare a codului în Latte. Să r ```latte ``` diff --git a/latte/ru/template-inheritance.texy b/latte/ru/template-inheritance.texy index 70d52a31db..9ebdb2631f 100644 --- a/latte/ru/template-inheritance.texy +++ b/latte/ru/template-inheritance.texy @@ -603,7 +603,7 @@ Hi, I am Mary. ```latte ``` diff --git a/latte/sl/template-inheritance.texy b/latte/sl/template-inheritance.texy index d0d873f230..b75a7b2e0b 100644 --- a/latte/sl/template-inheritance.texy +++ b/latte/sl/template-inheritance.texy @@ -603,7 +603,7 @@ V sistemu Latte obstajajo različne vrste dedovanja in ponovne uporabe kode. Za ```latte ``` diff --git a/latte/tr/template-inheritance.texy b/latte/tr/template-inheritance.texy index 5cb21fc43a..38a30ed87b 100644 --- a/latte/tr/template-inheritance.texy +++ b/latte/tr/template-inheritance.texy @@ -603,7 +603,7 @@ Latte'de çeşitli kalıtım ve kod yeniden kullanımı türleri vardır. Daha f ```latte ``` diff --git a/latte/uk/template-inheritance.texy b/latte/uk/template-inheritance.texy index 45d508d970..54f311aff4 100644 --- a/latte/uk/template-inheritance.texy +++ b/latte/uk/template-inheritance.texy @@ -603,7 +603,7 @@ Hi, I am Mary. ```latte ``` diff --git a/quickstart/bg/@home.texy b/quickstart/bg/@home.texy index b9950b8bb4..ee5ea68b1f 100644 --- a/quickstart/bg/@home.texy +++ b/quickstart/bg/@home.texy @@ -77,7 +77,7 @@ http://localhost/nette-blog/www/ Почистване .[#toc-cleanup] ========================== -Уеб проектът съдържа начална страница, която можем да премахнем - не се колебайте да замените съдържанието на файла `app/Presenters/templates/Homepage/default.latte` с текста `Hello world!`. +Уеб проектът съдържа начална страница, която можем да премахнем - не се колебайте да замените съдържанието на файла `app/Presenters/templates/Home/default.latte` с текста `Hello world!`. [* qs-hello.webp .{url:-} *] @@ -86,7 +86,7 @@ http://localhost/nette-blog/www/ Tracy (дебъгер) .[#toc-tracy-debugger] ====================================== -Изключително важен инструмент за разработка е [дебъгер, наречен Tracy. |tracy:] Опитайте се да направите някои грешки във вашия файл `app/Presenters/HomepagePresenter.php` (например да премахнете къдравата скоба от дефиницията на класа HomepagePresenter) и вижте какво ще се случи. Ще се появи страница с червен екран и разбираемо описание на грешката. +Изключително важен инструмент за разработка е [дебъгер, наречен Tracy. |tracy:] Опитайте се да направите някои грешки във вашия файл `app/Presenters/HomePresenter.php` (например да премахнете къдравата скоба от дефиницията на класа HomePresenter) и вижте какво ще се случи. Ще се появи страница с червен екран и разбираемо описание на грешката. [* qs-tracy.webp .{url:-}(debugger screen) *] diff --git a/quickstart/bg/authentication.texy b/quickstart/bg/authentication.texy index 86c1b213b3..85d904fb3c 100644 --- a/quickstart/bg/authentication.texy +++ b/quickstart/bg/authentication.texy @@ -82,7 +82,7 @@ public function signInFormSucceeded(Form $form, \stdClass $data): void { try { $this->getUser()->login($data->username, $data->password); - $this->redirect('Homepage:'); + $this->redirect('Home:'); } catch (Nette\Security\AuthenticationException $e) { $form->addError('Неправильные логин или пароль.'); @@ -117,7 +117,7 @@ public function startup(): void Скриване на връзки .[#toc-hide-links] ------------------------------------- -Неоторизиран потребител вече не може да вижда страниците за създаване и редактиране, но все още може да вижда връзките, които сочат към тях. Нека скрием и тях. Една такава връзка се намира на адрес `app/Presenters/templates/Homepage/default.latte`, като тя трябва да бъде видима само ако потребителят е влязъл в системата. +Неоторизиран потребител вече не може да вижда страниците за създаване и редактиране, но все още може да вижда връзките, които сочат към тях. Нека скрием и тях. Една такава връзка се намира на адрес `app/Presenters/templates/Home/default.latte`, като тя трябва да бъде видима само ако потребителят е влязъл в системата. Можем да го скрием, като използваме *n:атрибута*, наречен `n:if`. Ако изявлението в него е `false`, тогава целият таг `
` и съдържанието му няма да бъде показано: @@ -142,7 +142,7 @@ public function startup(): void ```latte .{file:app/Presenters/templates/@layout.latte} ...