Продолжаем публиковать 30 лучших практик для symfony – часть вторая (часть первая, часть третья).
#11
- Выгружайте в продуктовую среду только production контроллеры.
ПЛОХО
А откройте-ка в google ссылочку http://www.google.ru/#hl=ru&source=hp&q=… Вы увидите что-то такое:
Прониклись? А вот так?
Ooops )) Побежал исправлять на продуктовых сайтах // hudson
#12
В идеале:
- Типичный метод контроллера не должен содержать более 30 строк кода;
- Типичный класс контроллера не должен содержать более ~15 методов экшенов.
В оригинале для иллюстрации BAD был приведен скрин огроменного куска кода метода (экранов на 10) , который думаю каждый в состоянии представить ) Где такой взять не знаю – поэтому продолжим рассматривать прочие практики // hudson
#13
- Всегда применяйте согласованные стандарты кодирования (как минимум непротиворечивые и одобренные командой);
- Всегда применяйте стандарты кодирования, принятые в Symfony (http://trac.symfony-project.org/wiki/HowToContributeToSymfony#CodingStandards).
А еще неплохо проверять следование стандартам посредством code-review вручную (сойдет, но дорого и субъективно), полуавтоматически (уже терпимо) или полностью автоматически (почти идеально )))) // hudson
#14
- Работа с постоянными сессиями должна быть делегирована методам класса, унаследованного от sfUser и только там.
ПЛОХО
// контроллер public function executeMakeCoffee( sfWebRequest $request ) { $coffeeBeans = $request->getParameter( 'beans', array() ); $coffeeDrops = array(); foreach( $coffeeBeans as $bean ) { $coffeeDrops[] = CoffeeMaker::mill( $bean )->infuse(); } $this->getUser()->setAttribute( 'coffee', implode( '~', $coffeeDrops ) ); }
ХОРОШО
// контроллер public function executeMakeCoffee( sfWebRequest $request ) { $this->getUser()->makeCoffee( $request->getParameter( 'beans', array() ) ); } // myUser или его наследник public function makeCoffee( $beans ) { $coffeeDrops = array(); foreach( $coffeeBeans as $bean ) { $coffeeDrops[] = CoffeeMaker::mill( $bean )->infuse(); } $this->setAttribute( 'coffee', implode( '~', $coffeeDrops ) ); }
#15
- Никогда не сериализуйте объекты в сессию
ПЛОХО
// контроллер public function executeRecentlySeenProducts( sfWebRequest $request ) { $this->products = Doctrine::getTable( 'Product' ) ->createQuery( 'p' ) ->where( 'is_published = 1' ) ->limit( 10 ) ->execute(); // Doctrine_Collection!!! $this->getUser()->setAttribute( 'recentlySeenProducts', $this->products ); }
ХОРОШО
// контроллер public function executeRecentlySeenProducts( sfWebRequest $request ) { $this->products = Doctrine::getTable( 'Product' ) ->createQuery( 'p' ) ->select( 'p.id' ) ->where( 'is_published = 1' ) ->limit( 10 ) ->fetchArray(); // array(1, 2, 3) $this->getUser()->setAttribute( 'recentlySeenProducts', $this->products ); }
#16
- Всегда кастомизируйте страницы ошибок 404 и 500
В общем-то об этом легко забыть. И еще легче потом откладывать от итерации к итерации )) //hudson
#17
- Никогда не используйте sfContext в ваших моделях (например sfContext::getInstance()):
- Потому что экземпляр sfContext, который вы получите, может сильно разнится в зависимости от окружения (cli, test, dev, prod);
- Это сделает ваш код намного менее тестируемым;
- Да, это трудно и тяжко.
ПЛОХО
// модель class Story extends BaseStory { public function save( $con = null ) { $this->created_by = sfContext::getInstance()->getUser()->getId(); return parent::save( $con ); } } // контроллер class storyActions extends sfActions { public function executeCreate( $request ) { $story = new Story(); $story->fromArray( $request->getParameter( 'story' ) ); $story->save(); } }
ПЛОХО
// модель class Story extends BaseStory { public function __construct( sfContext $context ) { $this->context = $context; } public function save( $con = null ) { $this->created_by = $this->context->getUser()->getId(); return parent::save( $con ); } } // контроллер class storyActions extends sfActions { public function executeCreate( $request ) { $story = new Story( sfContext::getInstance() ); $story->fromArray( $request->getParameter( 'story' ) ); $story->save(); } }
ХОРОШО
// модель class Story extends BaseStory { protected $sf_user = null; public function __construct( sfUser $sf_user ) { $this->sf_user = $sf_user; } public function save( $con = null ) { if( $this->sf_user instanceof sfBasicSecurityUser ) { $this->created_by = $this->sf_user->getId(); } return parent::save( $con ); } } // контроллер class storyActions extends sfActions { public function executeCreate( $request ) { $story = new Story( $this->getUser() ); $story->fromArray( $request->getParameter( 'story' ) ); $story->save(); } }
ХОРОШО
// модель class Story extends BaseStory { } // контроллер class storyActions extends sfActions { public function executeCreate( $request ) { $story = new Story(); $story->fromArray( array_merge( $request->getParameter( 'story' ), array( 'created_by' => $this->getUser()->getId() ) ) ); $story->save(); } }
#18
- Старайтесь не создавать большие общие классы утилит с большим количеством статических методов;
- Потому что это выглядит как возврат к процедурному программированию )))
- Пишите строго специализированные классы.
#19
- Используйте sfLogger для отладки, вместо привычного echo’анья или отладочных сообщений;
- Кастомные логгеры FirePHP или FireSymfony могут быть еще более полезны при отладке приложения (например веб-сервисов).
#20
- Если исходные коды находятся в системе контроля версий (SCM tool), то файлы проекта не должны содержать паролей, локальных путей и т.п.;
- Файлы, содержащие указанные выше данные, должны быть добавлены в директиву ignore;
- Типичный пример: databases.yml;
- Также надо распределять платформо-зависимые конфигурационные файлы.
Ну что же, окончание следует )))
Убежал закрывать свои dev контроллеры.
Да, не помешает ) Кстати, на хабре еще мелькала заметка про доступность директорий .svn – тоже “хороший тон” веб-разработки закрывать и их от постороннего глаза.
“Хороший тон” пользоваться svn export’ом 😉
svn-export’ом на мой взгляд не так удобно выносить патч на боевые серверы. Или я не умею его готовить?
#17
* Никогда не используйте sfContext в ваших моделях (например sfContext::getInstance())
а есть аналогичный пример только с update, а не create и не на самом объекте, а с $form->save() ?