forked from maxim-oleinik/symfony-dev-rules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsymfony.txt
224 lines (168 loc) · 11 KB
/
symfony.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
Требования/соглашения для работы с symfony
================================================================================
1. Общее
--------
1.1 Для собственных классов ставим префикс "my"
Например: mySuperClass или myWidgetFormCustomInput
1.2 Генерация html ТОЛЬКО в шаблонах или в хелперах.
1.3 Не использовать sfContext::getInstance()
Все внешние зависимости передавать явно через конструктор, сеттеры, опции и пр.
Бывают исключения, когда без контекста не обойтись, но надо четко понимать разницу
между оправданным исключением и хаком.
2. Конфиг
---------
2.1 ИСПОЛЬЗОВАТЬ конфиг, а не хардкодить в разных местах
2.2 Использовать каскады конфигурации: plugin -> project -> app -> module
2.3 Использовать возможности конфигурирования для разных ENV (prod, dev, test)
2.4 Всегда включаем защиту от csrf и xss. Отдельные классы, например DateTime,
можно добавить в исключение:
sfOutputEscaper::markClassesAsSafe(array(
'DateTime',
));
3. Роутинг
----------
3.1 Не использовать дефолты в правилах для модуля и экшена.
Всегда прописывать явно или указывать перечисление в requirements.
Так НЕ надо делать:
article:
url: /article/:action
params: { module: article }
default:
url: /:module/:action/*
OK: Явно указать ограничение:
article:
url: /article/:action
params: { module: article }
requirements:
action: (?:show|hide)
3.2 Если мы используем частичный RouteCollection, тогда явно указываем, что мы используем:
article:
class: sfDoctrineRouteCollection
options:
module: article
model: Article
actions: [new, create, edit, update, delete]
3.3 Используем sfDoctrineRoute и sfDoctrineRouteCollection, чтобы в контроллере
выбирать объект через
$this->getRoute()->getObject()
Фреймворк автоматически выкинет 404 ошибку, если объект не будет найден.
3.4 Для контроля за методами запроса используем requirements:
requirements:
sf_method: [get, head]
sf_method: post
sf_method: put
sf_method: delete
Для "get" запросов желательно оставлять возможность "head" запросов. Поисковики все-таки.
3.5 Используем requirements, чтобы лимитировать sf_format:
requirements:
sf_format: html
sf_format: (?:html|xml|json)
В противном случае symfony выкинет исключение, если не найдет шаблон для указанного формата,
который мы не поддерживаем. 404 лучше чем 500.
4. Контроллер
-------------
4.1 В контроллерах не пришем запросы - по максимуму выносим в таблицы и в фильтры.
4.2 Используем всегда, когда это возможно: $this->getRoute()->getObject()
4.3 phpdoc для класса и каждого метода. Так наглядно, чисто и удобно.
/**
* Список статей
*/
public function executeIndex(sfRequest $request)
4.4 Контроллеры не должны содержать бизнес-логики, вычислений. Все выносить в модели
и отдельные классы. Длинный метод - индикатор перегрузки.
5. Шаблоны
----------
5.1 Шаблоны не должны содержать никакой бизнес логики.
Только логика (if) на уровне отображения. Но и ее следует сремиться минимизировать,
чтобы не загромождать шаблоны.
5.2 Использовать партиалы, чтобы:
- не копипастить и повторно использовать фрагменты html-кода
- сделать шаблон чище, короче и понятнее
5.3 Использовать хелперы.
Можно сделать себе подсказку: grep 'function' ./lib/vendor/symfony/lib/helper/*
5.4 Пишем свои хелперы, чтобы:
- не копипастить
- вынести часть логики отображения из шаблона
- сделать шаблон чище, короче и понятнее
5.5 Ссылки и урлы выводим ТОЛЬКО через хелперы и ТОЛЬКО с явным указанием правила маршрутизации:
OK:
url_for('article_edit', $article);
url_for('article_edit', array('id' => $article->getId());
NO:
url_for('article/edit', $article);
url_for('@article_edit?id='.$article->getId());
Бывают случаи, когда стоит использовать именно @article_edit?id=...
Но эти случаи довольно редки и следует стремиться не использовать такую форму записи.
Routing.yml - единственное место, где фигурируют реальные URL.
В тестах мы используем $this->generateUrl(), см. sfPHPUnitFunctionalTestCase
5.6 Используем только альтернативный синтаксис для PHP:
<?php foreach ($list as $item): ?>
Никаких фигурных скобок (с учетом оправданных исключений).
5.7 В заголовке шаблона указываем phpdoc с описанием и списком ожидаемых переменных:
<?php
/**
* Список статей
*
* @param array Article $articles
*/
?>
5.8 Все стили только в css.
6. Модель/Таблицы
-----------------
6.1 Желательно, чтобы в таблицах методы возвращали Doctrine_Query,
чтобы контроллер или другой клиент мог самостоятельно выбрать способ гидрации
или уточнить запрос.
А еще лучше читаем и делаем:
http://prendreuncafe.com/blog/post/Optimize-your-Doctrine-Workflow-with-Specialized-Queries
7. БД/schema.yml
----------------
7.1 Таблицам даем название во множественном числе:
Например:
Tag:
tableName: tags
Article:
tableName: articles
ArticleTag:
tableName: article_tags
7.2 Именования колонок:
- первичный ключ: "id"
- связь с другой таблицей по первичному ключу: "user_id", "article_id", "tag_id"
- timestamp: "created_at", "updated_at", "deleted_at"
7.3 Для первичных ключей используем по смыслу:
type: integer(4), unsigned: false
type: integer(3)
type: integer(2) # +-32767
type: integer(1) # +-127
До бигинта integer(8) редко кто доживает, а кто доживает, тот понимает, что это уже float.
7.4 Первичные ключи не объявляем в схеме там, где это возможно.
Доктрина самостоятельно добавит "id" и укажет все необходимые свойства.
Правда укажет тип integer(8), но если мы используем миграции, это не принципиально.
В миграции, мы можем указать свой тип integer(4).
8. Миграции
-----------
Подробно см. http://habrahabr.ru/blogs/symfony/97940/
8.1 Миграции должны быть атомарными. Одна правка — одна миграция.
ОБЯЗАТЕЛЬНО выносить все правки FK в отдельную миграцию.
8.2 Названия
В названии файла и класса миграции указываем номер версии, модель, действие и описание:
001_Article_CreateTable.php
002_Article_AddColumn_AuthorId.php
003_Article_AddFk_Authors.php
004_Article_UpdateColumn_Title.php
005_Article_DropTable.php
класс:
class Migration_Article_CreateTable
8.3 Если есть возможность, используем короткую запись миграций вместе с migrate().
См. http://www.doctrine-project.org/projects/orm/1.2/docs/manual/migrations/en#writing-migration-classes:up/down-automation
8.4 ВСЕГДА делаем возможность откатить миграцию.
8.5 Миграции данных пишем ТОЛЬКО в raw sql и не используем модели.
Пишем в pre/post хуках.
TODO
--------------------------------------------------------------------------------
- расширенный naming convention для классов
- Указывать AppNamе: app/lib/myAdminUser.php
- BaseClass, ArticleQuery (myAdminArticleQuery)
- В первую очередь класть классы в app/lib, а потом выносить в глобальный lib при первой необходимости.
- schema.yml: Всегда указывать relations: autoComplete: false, и включать при первой необходимости.
- i18N
- генерация админки