in Профессиональное

30 лучших практик для Symfony – часть вторая #11 – #20

Продолжаем публиковать 30 лучших практик для symfony – часть вторая (часть первая, часть третья).

#11

  • Выгружайте в продуктовую среду только production контроллеры.

ПЛОХО

А откройте-ка в google ссылочку http://www.google.ru/#hl=ru&source=hp&q=… Вы увидите что-то такое:

symfony_frontend_dev_oops

Прониклись? А вот так?

symfony_frontend_dev_oops_2

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;
  • Также надо распределять платформо-зависимые конфигурационные файлы.

Ну что же, окончание следует )))

Write a Comment

Comment

ERROR: si-captcha.php plugin: GD image support not detected in PHP!

Contact your web host and ask them to enable GD image support for PHP.

ERROR: si-captcha.php plugin: imagepng function not detected in PHP!

Contact your web host and ask them to enable imagepng for PHP.

  1. Да, не помешает ) Кстати, на хабре еще мелькала заметка про доступность директорий .svn – тоже “хороший тон” веб-разработки закрывать и их от постороннего глаза.

      • svn-export’ом на мой взгляд не так удобно выносить патч на боевые серверы. Или я не умею его готовить?

  2. #17
    * Никогда не используйте sfContext в ваших моделях (например sfContext::getInstance())

    а есть аналогичный пример только с update, а не create и не на самом объекте, а с $form->save() ?