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

Магические числа и таблицы-справочники в Doctrine/Propel

Кросспост моего хабратопика.

Коль скоро у вас в проекте используется база данных, то вам рано или поздно потребуются справочные таблицы. Такие таблицы я бы условно разбил на три категории:

  1. Небольшие справочники, до 10, реже 20 записей. Например – таблица статусов чего-нибудь (active|inactive|deleted как минимум).
  2. Средние справочники – от 20 до нескольких сотен записей. Например, таблица типов или категорий чего-либо.
  3. Большие справочники – от нескольких сотен до сотен тысяч записей. Например список городов и улиц России.

Справочники, как правило, заполняются разово при создании и крайне редко пополняются. Но тем не менее, пополнение возможно и наиболее вероятно для третьего типа, менее для второго и редко для первого.

Собственно зачем я это пишу:

Раз у вас есть таблица, то есть и классы модели для нее. Все хорошо, пока вам в коде не приходится сослаться на какое-то значение из справочника. Например есть таблица Status:

|    id    |     name       |
----------------------------
|    1     |     active     |
|    2     |     inactive   |
|    3     |     deleted    |

В связанных таблицах мы имеем HasOne: Status, и foreign key на status.id.

И вот нам понадобилось выбрать все активные записи. Не долго думая мы пишем:

$query = Doctrine_Query::create()
  ->select()
  ->from( "Product p, p.Status s" )
  ->where( "s.name = 'active'" )
//...

Oops. Джойн хорош, но нам постоянно добавлять его к любому запросу неинтересно. “Рефакторим”:

$query = Doctrine_Query::create()
  ->select()
  ->from( "Product p" )
  ->where( "p.status_id = 1" )
//...

OOOOOooops. По проекту плодятся магические числа:

status_id = 2
type_id = 36
city_id = 1234

Стоп.

а) мы не хотим в каждый запрос, который требует связки со справочником пихать join (да и name=”active” тоже магическая константа по сути, но вербально понятная).
б) мы не хотим плодить магические числа.

И какой же выход?

Честно говоря, красивого выхода я не вижу. Для справочников типа 1 я применяю следующий подход:

// Status
class StatusTable extends Doctrine_Table
{
  const STATUS_ACTIVE   = 1;
  const STATUS_INACTIVE = 2;
  const STATUS_DELETED  = 3;
  ...
}
// Product
class ProductTable extends Doctrine_Table
{
  public function getActive()
  {
    ...
    ->addWhere( "p.status_id = ?", self::STATUS_ACTIVE ) // ну или как-то так :)
  }
  ...
}

Этот подход более-менее работает только со справочниками типа 1 (при этом, при добавлении нового статуса попробуй еще вспомнить, что надо добавить еще одну константу, благо это не часто требуется).

Со справочниками типов 2 и 3 вообще не знаю что делать.

А как вы работаете со справочниками?

P.S. не факт что со справочниками типа 3 приходится работать “точечно”, поэтому для них может это и не актуально.

Write a Comment

Comment

*