Skip to content

Commit dfe6981

Browse files
Copilotsamdark
andauthored
Fix #137: Add explanation of private properties benefits in DI container guide
Co-authored-by: Alexander Makarov <[email protected]>
1 parent 73bae4d commit dfe6981

File tree

4 files changed

+204
-58
lines changed

4 files changed

+204
-58
lines changed

guide/en/concept/di-container.md

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,38 @@ There are many ways to perform it:
7676
- Method injection. Best for optional dependencies.
7777
- Property injection. Better to be avoided in PHP except maybe data transfer objects.
7878

79+
### Why use private properties <span id="why-private-properties"></span>
80+
81+
In the composition example above, note that the `$cache` property is declared as `private`.
82+
83+
This approach embraces composition by ensuring objects have well-defined interfaces for interaction rather than
84+
direct property access, making the code more maintainable and less prone to certain types of mistakes.
85+
86+
This design choice provides several benefits:
87+
88+
- **Encapsulation**: Private properties with getters/setters allow you to control access and make future changes
89+
without breaking existing code.
90+
- **Data integrity**: Setters can validate, normalize, or format values before storing them, ensuring properties
91+
contain valid data.
92+
- **Immutability**: Private properties enable immutable object patterns where setter `with*()` methods return
93+
new instances rather than modifying the current one.
94+
- **Flexibility**: You can create read-only or write-only properties or add additional logic to property access later.
95+
7996

8097
## DI container <span id="di-container"></span>
8198

82-
Injecting basic dependencies is simple and easy. You're choosing a place where you don't care about dependencies,
99+
Injecting basic dependencies is straightforward. You're choosing a place where you don't care about dependencies,
83100
which is usually an action handler, which you aren't going to unit-test ever, create instances of dependencies needed
84101
and pass these to dependent classes.
85102

86-
It works well when there aren't many dependencies overall and when there are no nested dependencies. When there are
103+
It works well when there are few dependencies overall and when there are no nested dependencies. When there are
87104
many and each dependency has dependencies itself, instantiating the whole hierarchy becomes a tedious process, which
88-
requires lots of code and may lead to hard to debug mistakes.
105+
requires lots of code and may lead to hardly debuggable mistakes.
89106

90-
Additionally, lots of dependencies, such as certain third party API wrapper, are the same for any class using it.
107+
Additionally, lots of dependencies, such as certain third-party API wrappers, are the same for any class using it.
91108
So it makes sense to:
92109

93-
- Define how to instantiate such API wrapper once.
110+
- Define how to instantiate such an API wrapper.
94111
- Instantiate it when required and only once per request.
95112

96113
That's what dependency containers are for.
@@ -170,9 +187,10 @@ return [
170187

171188
### Injecting dependencies <span id="injecting-dependencies"></span>
172189

173-
Directly referencing container in a class is a bad idea since the code becomes non-generic, coupled to container interface
174-
and, what's worse, dependencies are becoming hidden. Because of that, Yii inverts the control by automatically injecting
175-
objects from a container in some constructors and methods based on method argument types.
190+
Directly referencing a container in a class is a bad idea since the code becomes non-generic,
191+
coupled to the container interface and, what's worse, dependencies are becoming hidden.
192+
Because of that, Yii inverts the control by automatically injecting objects from a container in some constructors
193+
and methods based on method argument types.
176194

177195
This is primarily done in constructor and handing method of action handlers:
178196

@@ -202,9 +220,9 @@ final readonly class MyController
202220
```
203221

204222
Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that instantiates and calls action handler, it
205-
checks the constructor and method argument types, get dependencies of these types from a container and pass them as
223+
checks the constructor and method argument types, gets dependencies of these types from a container and passes them as
206224
arguments. That's usually called auto-wiring. It happens for sub-dependencies as well, that's if you don't give dependency
207-
explicitly, container would check if it has such a dependency first.
225+
explicitly, the container would check if it has such a dependency first.
208226
It's enough to declare a dependency you need, and it would be got from a container automatically.
209227

210228

guide/po/concept_di-container.md/concept_di-container.md.pot

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
msgid ""
88
msgstr ""
99
"Project-Id-Version: PACKAGE VERSION\n"
10-
"POT-Creation-Date: 2025-09-04 07:37+0500\n"
10+
"POT-Creation-Date: 2025-09-05 12:06+0000\n"
1111
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1212
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313
"Language-Team: LANGUAGE <[email protected]>\n"
@@ -146,6 +146,61 @@ msgid ""
146146
"objects."
147147
msgstr ""
148148

149+
#. type: Title ###
150+
#: en/concept/di-container.md
151+
#, no-wrap
152+
msgid "Why use private properties <span id=\"why-private-properties\"></span>"
153+
msgstr ""
154+
155+
#. type: Plain text
156+
#: en/concept/di-container.md
157+
msgid ""
158+
"In the composition example above, note that the `$cache` property is "
159+
"declared as `private`."
160+
msgstr ""
161+
162+
#. type: Plain text
163+
#: en/concept/di-container.md
164+
msgid ""
165+
"This approach embraces composition by ensuring objects have well-defined "
166+
"interfaces for interaction rather than direct property access, making the "
167+
"code more maintainable and less prone to certain types of mistakes."
168+
msgstr ""
169+
170+
#. type: Plain text
171+
#: en/concept/di-container.md
172+
msgid "This design choice provides several benefits:"
173+
msgstr ""
174+
175+
#. type: Bullet: '- '
176+
#: en/concept/di-container.md
177+
msgid ""
178+
"**Encapsulation**: Private properties with getters/setters allow you to "
179+
"control access and make future changes without breaking existing code."
180+
msgstr ""
181+
182+
#. type: Bullet: '- '
183+
#: en/concept/di-container.md
184+
msgid ""
185+
"**Data integrity**: Setters can validate, normalize, or format values before "
186+
"storing them, ensuring properties contain valid data."
187+
msgstr ""
188+
189+
#. type: Bullet: '- '
190+
#: en/concept/di-container.md
191+
msgid ""
192+
"**Immutability**: Private properties enable immutable object patterns where "
193+
"setter `with*()` methods return new instances rather than modifying the "
194+
"current one."
195+
msgstr ""
196+
197+
#. type: Bullet: '- '
198+
#: en/concept/di-container.md
199+
msgid ""
200+
"**Flexibility**: You can create read-only or write-only properties or add "
201+
"additional logic to property access later."
202+
msgstr ""
203+
149204
#. type: Title ##
150205
#: en/concept/di-container.md
151206
#, no-wrap
@@ -155,7 +210,7 @@ msgstr ""
155210
#. type: Plain text
156211
#: en/concept/di-container.md
157212
msgid ""
158-
"Injecting basic dependencies is simple and easy. You're choosing a place "
213+
"Injecting basic dependencies is straightforward. You're choosing a place "
159214
"where you don't care about dependencies, which is usually an action handler, "
160215
"which you aren't going to unit-test ever, create instances of dependencies "
161216
"needed and pass these to dependent classes."
@@ -164,22 +219,23 @@ msgstr ""
164219
#. type: Plain text
165220
#: en/concept/di-container.md
166221
msgid ""
167-
"It works well when there aren't many dependencies overall and when there are "
168-
"no nested dependencies. When there are many and each dependency has "
222+
"It works well when there are few dependencies overall and when there are no "
223+
"nested dependencies. When there are many and each dependency has "
169224
"dependencies itself, instantiating the whole hierarchy becomes a tedious "
170-
"process, which requires lots of code and may lead to hard to debug mistakes."
225+
"process, which requires lots of code and may lead to hardly debuggable "
226+
"mistakes."
171227
msgstr ""
172228

173229
#. type: Plain text
174230
#: en/concept/di-container.md
175231
msgid ""
176-
"Additionally, lots of dependencies, such as certain third party API wrapper, "
177-
"are the same for any class using it. So it makes sense to:"
232+
"Additionally, lots of dependencies, such as certain third-party API "
233+
"wrappers, are the same for any class using it. So it makes sense to:"
178234
msgstr ""
179235

180236
#. type: Bullet: '- '
181237
#: en/concept/di-container.md
182-
msgid "Define how to instantiate such API wrapper once."
238+
msgid "Define how to instantiate such an API wrapper."
183239
msgstr ""
184240

185241
#. type: Bullet: '- '
@@ -315,9 +371,9 @@ msgstr ""
315371
#. type: Plain text
316372
#: en/concept/di-container.md
317373
msgid ""
318-
"Directly referencing container in a class is a bad idea since the code "
319-
"becomes non-generic, coupled to container interface and, what's worse, "
320-
"dependencies are becoming hidden. Because of that, Yii inverts the control "
374+
"Directly referencing a container in a class is a bad idea since the code "
375+
"becomes non-generic, coupled to the container interface and, what's worse, "
376+
"dependencies are becoming hidden. Because of that, Yii inverts the control "
321377
"by automatically injecting objects from a container in some constructors and "
322378
"methods based on method argument types."
323379
msgstr ""
@@ -361,9 +417,9 @@ msgstr ""
361417
msgid ""
362418
"Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that "
363419
"instantiates and calls action handler, it checks the constructor and method "
364-
"argument types, get dependencies of these types from a container and pass "
420+
"argument types, gets dependencies of these types from a container and passes "
365421
"them as arguments. That's usually called auto-wiring. It happens for sub-"
366-
"dependencies as well, that's if you don't give dependency explicitly, "
422+
"dependencies as well, that's if you don't give dependency explicitly, the "
367423
"container would check if it has such a dependency first. It's enough to "
368424
"declare a dependency you need, and it would be got from a container "
369425
"automatically."

guide/po/concept_di-container.md/ru/concept_di-container.md.ru.po

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
msgid ""
77
msgstr ""
88
"Project-Id-Version: \n"
9-
"POT-Creation-Date: 2025-09-04 07:37+0500\n"
9+
"POT-Creation-Date: 2025-09-05 12:06+0000\n"
1010
"PO-Revision-Date: 2025-09-04 07:50+0500\n"
1111
"Last-Translator: Automatically generated\n"
1212
"Language-Team: none\n"
@@ -189,6 +189,48 @@ msgstr "Через метод. Лучше использовать для нео
189189
msgid "Property injection. Better to be avoided in PHP except maybe data transfer objects."
190190
msgstr "Через свойство. Лучше избегать использования в PHP, за исключением, может быть, объектов передачи данных (DTO)"
191191

192+
#. type: Title ###
193+
#: en/concept/di-container.md
194+
#, fuzzy, no-wrap
195+
#| msgid "References <span id=\"references\"></span>"
196+
msgid "Why use private properties <span id=\"why-private-properties\"></span>"
197+
msgstr "Полезные ссылки <span id=\"references\"></span>"
198+
199+
#. type: Plain text
200+
#: en/concept/di-container.md
201+
msgid "In the composition example above, note that the `$cache` property is declared as `private`."
202+
msgstr ""
203+
204+
#. type: Plain text
205+
#: en/concept/di-container.md
206+
msgid "This approach embraces composition by ensuring objects have well-defined interfaces for interaction rather than direct property access, making the code more maintainable and less prone to certain types of mistakes."
207+
msgstr ""
208+
209+
#. type: Plain text
210+
#: en/concept/di-container.md
211+
msgid "This design choice provides several benefits:"
212+
msgstr ""
213+
214+
#. type: Bullet: '- '
215+
#: en/concept/di-container.md
216+
msgid "**Encapsulation**: Private properties with getters/setters allow you to control access and make future changes without breaking existing code."
217+
msgstr ""
218+
219+
#. type: Bullet: '- '
220+
#: en/concept/di-container.md
221+
msgid "**Data integrity**: Setters can validate, normalize, or format values before storing them, ensuring properties contain valid data."
222+
msgstr ""
223+
224+
#. type: Bullet: '- '
225+
#: en/concept/di-container.md
226+
msgid "**Immutability**: Private properties enable immutable object patterns where setter `with*()` methods return new instances rather than modifying the current one."
227+
msgstr ""
228+
229+
#. type: Bullet: '- '
230+
#: en/concept/di-container.md
231+
msgid "**Flexibility**: You can create read-only or write-only properties or add additional logic to property access later."
232+
msgstr ""
233+
192234
#. type: Title ##
193235
#: en/concept/di-container.md
194236
#, no-wrap
@@ -197,28 +239,36 @@ msgstr "Контейнер внедрения зависимостей <span id=
197239

198240
#. type: Plain text
199241
#: en/concept/di-container.md
200-
msgid "Injecting basic dependencies is simple and easy. You're choosing a place where you don't care about dependencies, which is usually an action handler, which you aren't going to unit-test ever, create instances of dependencies needed and pass these to dependent classes."
242+
#, fuzzy
243+
#| msgid "Injecting basic dependencies is simple and easy. You're choosing a place where you don't care about dependencies, which is usually an action handler, which you aren't going to unit-test ever, create instances of dependencies needed and pass these to dependent classes."
244+
msgid "Injecting basic dependencies is straightforward. You're choosing a place where you don't care about dependencies, which is usually an action handler, which you aren't going to unit-test ever, create instances of dependencies needed and pass these to dependent classes."
201245
msgstr ""
202246
"Внедрять базовые зависимости просто и легко.\n"
203247
"Вы выбираете место, где вас не волнуют зависимости, которые обычно являются обработчиками действий и которые вы не собираетесь тестировать, создаете экземпляры необходимых зависимостей и передаете их в зависимые классы."
204248

205249
#. type: Plain text
206250
#: en/concept/di-container.md
207-
msgid "It works well when there aren't many dependencies overall and when there are no nested dependencies. When there are many and each dependency has dependencies itself, instantiating the whole hierarchy becomes a tedious process, which requires lots of code and may lead to hard to debug mistakes."
251+
#, fuzzy
252+
#| msgid "It works well when there aren't many dependencies overall and when there are no nested dependencies. When there are many and each dependency has dependencies itself, instantiating the whole hierarchy becomes a tedious process, which requires lots of code and may lead to hard to debug mistakes."
253+
msgid "It works well when there are few dependencies overall and when there are no nested dependencies. When there are many and each dependency has dependencies itself, instantiating the whole hierarchy becomes a tedious process, which requires lots of code and may lead to hardly debuggable mistakes."
208254
msgstr ""
209255
"Это хорошо работает, когда в целом зависимостей немного и нет вложенных зависимостей.\n"
210256
"Когда их много, и каждая зависимость сама имеет зависимости, создание всей иерархии становится утомительным процессом, который требует большого количества кода и может привести к трудно отлаживаемым ошибкам."
211257

212258
#. type: Plain text
213259
#: en/concept/di-container.md
214-
msgid "Additionally, lots of dependencies, such as certain third party API wrapper, are the same for any class using it. So it makes sense to:"
260+
#, fuzzy
261+
#| msgid "Additionally, lots of dependencies, such as certain third party API wrapper, are the same for any class using it. So it makes sense to:"
262+
msgid "Additionally, lots of dependencies, such as certain third-party API wrappers, are the same for any class using it. So it makes sense to:"
215263
msgstr ""
216264
"Кроме того, многие зависимости, такие как некоторые сторонние обертки API, одинаковы для любого класса, использующего его.\n"
217265
"Поэтому имеет смысл:"
218266

219267
#. type: Bullet: '- '
220268
#: en/concept/di-container.md
221-
msgid "Define how to instantiate such API wrapper once."
269+
#, fuzzy
270+
#| msgid "Define how to instantiate such API wrapper once."
271+
msgid "Define how to instantiate such an API wrapper."
222272
msgstr "Определить, как создать экземпляр такой обертки API один раз."
223273

224274
#. type: Bullet: '- '
@@ -390,7 +440,9 @@ msgstr "Внедрение зависимостей <span id=\"injecting-depende
390440

391441
#. type: Plain text
392442
#: en/concept/di-container.md
393-
msgid "Directly referencing container in a class is a bad idea since the code becomes non-generic, coupled to container interface and, what's worse, dependencies are becoming hidden. Because of that, Yii inverts the control by automatically injecting objects from a container in some constructors and methods based on method argument types."
443+
#, fuzzy
444+
#| msgid "Directly referencing container in a class is a bad idea since the code becomes non-generic, coupled to container interface and, what's worse, dependencies are becoming hidden. Because of that, Yii inverts the control by automatically injecting objects from a container in some constructors and methods based on method argument types."
445+
msgid "Directly referencing a container in a class is a bad idea since the code becomes non-generic, coupled to the container interface and, what's worse, dependencies are becoming hidden. Because of that, Yii inverts the control by automatically injecting objects from a container in some constructors and methods based on method argument types."
394446
msgstr "Непосредственное обращение к контейнеру в классе — плохая идея, так как код становится не универсальным, сопряжен с интерфейсом контейнера и, что еще хуже, зависимости становятся скрытыми. Поэтому Yii инвертирует управление, автоматически вводя объекты из контейнера в конструкторы и методы, основываясь на типах аргументов."
395447

396448
#. type: Plain text
@@ -449,7 +501,9 @@ msgstr ""
449501

450502
#. type: Plain text
451503
#: en/concept/di-container.md
452-
msgid "Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that instantiates and calls action handler, it checks the constructor and method argument types, get dependencies of these types from a container and pass them as arguments. That's usually called auto-wiring. It happens for sub-dependencies as well, that's if you don't give dependency explicitly, container would check if it has such a dependency first. It's enough to declare a dependency you need, and it would be got from a container automatically."
504+
#, fuzzy
505+
#| msgid "Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that instantiates and calls action handler, it checks the constructor and method argument types, get dependencies of these types from a container and pass them as arguments. That's usually called auto-wiring. It happens for sub-dependencies as well, that's if you don't give dependency explicitly, container would check if it has such a dependency first. It's enough to declare a dependency you need, and it would be got from a container automatically."
506+
msgid "Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that instantiates and calls action handler, it checks the constructor and method argument types, gets dependencies of these types from a container and passes them as arguments. That's usually called auto-wiring. It happens for sub-dependencies as well, that's if you don't give dependency explicitly, the container would check if it has such a dependency first. It's enough to declare a dependency you need, and it would be got from a container automatically."
453507
msgstr ""
454508
"Поскольку именно [yiisoft/injector](https://github.com/yiisoft/injector) создает экземпляр и вызывает обработчик действий - он проверяет типы аргументов конструктора и метода, получает зависимости этих типов из контейнера и передает их как аргументы.\n"
455509
"Обычно это называется автоматическим разрешением зависимостей.\n"

0 commit comments

Comments
 (0)