За последние 24 часа нас посетили 18203 программиста и 1685 роботов. Сейчас ищут 1111 программистов ...

Model, Mapper, Generator

Тема в разделе "Решения, алгоритмы", создана пользователем Padaboo, 10 ноя 2010.

  1. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    [vs]
    PHP:
    1. <?php
    2. $vasya = $mapper->findById(new User(), 9507);
    3. while(true){
    4.    $vasya->jump();
    5. }
    6.  
    :D
    угу, query же false вернет, если в уникальное поле попытаешься вставить совпадающий mail
    думаю что еше написать можно, libevent второй у меня явно не получится, хотя...
     
  2. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Dagdamor
    ты отстал от жизни :D
     
  3. Apple

    Apple Активный пользователь

    С нами с:
    13 янв 2007
    Сообщения:
    4.984
    Симпатии:
    2
    Угу, создать ненужный индекс? =)
     
  4. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Не ненужный. Спичечную оптимизацию фтопку
     
  5. Dagdamor

    Dagdamor Активный пользователь

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Mr.M.I.T.
    Не вижу в вашем сообщении аналогичного примера на ORM.
    Или это для него неподъемная задача? ;)
     
  6. Dagdamor

    Dagdamor Активный пользователь

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Padaboo
    Ищу то - не знаю что? ;) Тоже, имхо, кривоватый подход.
     
  7. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Dagdamor
    книжный подход, в точности по книге фаулера, только на php, мне он меньше понравился как то, по сути для каждой модели свой маппер, просто возвращает их фабрика
    PHP:
    1. <?php
    2. $finder      = new PersistenceFactory::getFinder('User');
    3. $idobj       = $factory->getIdentyObject()->field('name');
    4. $collection = $finder->find($idobj);
    5. foreach($collection as $user){
    6.    echo $user->name;
    7. }
     
  8. Dagdamor

    Dagdamor Активный пользователь

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Padaboo
    Ужас какой-то...
    Этот Фаулер случайно не родственник нашему Попову? Зачем так все усложнять?
     
  9. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Dagdamor
    ну как сказать, это шаблоны корпоративных приложений, изначальная реализация на java и C#
    в общем смысл в том, что у меня маппер выполняет слишком много функций
    там кроме того, есть ленивая загрузка коллекций т.е. подргружаются допустим $user->friends каждый из которых в свою очередь тоже объект, но только по мере надобности, когда вызывается delete для юзера удаляется все что было внутри
    ну и из за большого количества ссылок в массиве на объекты, скрипт разрастается и захлебывается, там добавлен регистр объектов, который следит, что бы одинаковых объектов в системе не было, например user с одинаковым id, все это большая куча кода и ее тяжело поддерживать, поэтому там через фабрики сделан

    потом вся работа с моделями декорируется, вот в это например
    PHP:
    1.  
    2. <?php
    3. UserService::delete($id);
    4. //удаляем все что с ним связанно
    5.  
     
  10. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    Dagdamor
    ах так, вот тебе =)

    буду переписывать твой говнокод на тру.
    PHP:
    1. <?php
    2. function makeFormYesNo($title, $description, $name, $value=1, $lang=true)
    3. {
    4.   global $language;
    5.   makeFormItem("header",$title,$description,$lang);
    6.   $value=$value?1:0;
    7.   $selected=array("","",$value=>" checked=\"checked\"");
    8.   echo "<input class=\"radio\" type=\"radio\" name=\"$name\" value=\"1\"$selected[1]> $language[admin_yes]&nbsp;\r\n";
    9.   echo "<input class=\"radio\" type=\"radio\" name=\"$name\" value=\"0\"$selected[0]> $language[admin_no]&nbsp;";
    10.   makeFormItem("footer");
    11. }
    PHP:
    1. <?php
    2. function makeFormYesNo($title, $description, $name,Laguage\Mutator $lang){
    3.     $form=new Form\Item(Form\Item::HEADER & Form\Item::FOOTER);
    4.     $form->SetTitle($title)
    5.              ->SetDesc($description)
    6.              ->SetLaguageMutator($lang);
    7.     $chbox1=new Form\Base\CheckBox(Form\Base\CheckBox::TYPE_RADIO,$name)
    8.                   ->SetValue(1)
    9.                   ->SetAttrs(Form\Base\CheckBox::ATTR_CHECKED);
    10.     $chbox2=new Form\Base\CheckBox(Form\Base\CheckBox::TYPE_RADIO,$name)->SetValue(0);
    11.     $form->AddItem($chbox1);
    12.     $form->AddText(Laguage::AddMutator($lang)->Get("Admin_Yes"));
    13.     $form->AddContext(Form\Base\HtmlEven::SPASE,Form\Base\HtmlEven::NEWLINE);
    14.     $form->AddItem($chbox2);
    15.     $form->AddText(Laguage::AddMutator($lang)->Get("Admin_No"));
    16.     $form->AddContext(Form\Base\HtmlEven::SPASE);
    17.     return $form->Html();
    18. }
    короче, на реализацию уйдет ещё пол года. пойду возьму зенд, ибо зачем изобретать свой велосипед? :D
     
  11. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    а у меня как то так было
    PHP:
    1. <?php
    2. Forms::form();
    3. Forms::input(array('name'=>'login'));
    4. Forms::input(array('name'=>'password','type'=>'password'));
    5. Forms::view();
    а потом решил, что это нафиг никому не надо
     
  12. Dagdamor

    Dagdamor Активный пользователь

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Mr.M.I.T.
    ))))))))))))))))))))))))))))))))))))

    А чо, в Зенд полноценные формы добавили? ;) Вроде ж не было их в палате царской.
     
  13. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Неее! Возьми Yii + Zend. Zend сам по себе это лего, только очень продвинутое. Его что бы юзать, надо рочень хорошо разбираться в паттернах и.т.д., уметь чётко проектировать. В общем слишком долго вливаться, лучше его как библиотеку.
     
  14. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    Падабу, ты правильно заметил, Фаулер писал с использованием java и c#, а ты пишешь НА php, поэтом код выглядит не в тему в какой-то степени
     
  15. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
  16. Духовность

    Духовность Активный пользователь

    С нами с:
    1 дек 2010
    Сообщения:
    4
    Симпатии:
    0
    А какой смысл в генерации класса модели? Почему нельзя наследовать весь общий функционал от абстрактного класса? Объявить члены-класса-таблицы ручками не сложнее, чем каждый раз делать перегенерацию после изменения структуры таблицы.
     
  17. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Духовность
    угу, я все вынес, когда пользоваться начал, просто топ забыл обновить, там не только это менял, как допереставлю систему - обновлю
     
  18. Духовность

    Духовность Активный пользователь

    С нами с:
    1 дек 2010
    Сообщения:
    4
    Симпатии:
    0
    Угу.

    Я в своей реализации подобного пошел чуть дальше:

    1. Через __call вызываю виртуальные методы моделей:
    PHP:
    1.  
    2. $user = new User();
    3. $user->setLogin('Test');
    4. $user->setPassword('qwerty');
    5. $mapper->add($user);
    зачем так было делать? Для унифицирования интерфейса, ведь не исключена ситуация, что нам потребуется ЯВНО реализовать методы моделей, например реализовать setPassword в виду какой-то логики:

    PHP:
    1.  
    2. fublic function setPassword($pass)
    3. {
    4.     if ($pass) $this->row['pass'] = md5($pass);
    5.  
    6.    // и т.п.
    7. }
    8.  
    т.е. я могу обращаться и присваивать свойства модели через $obj->prop_name, но в большинстве случаев работают виртуальные геттеры/сетеры.

    2. Все атрибуты модели имеют "карту" - это настройки вида:
    PHP:
    1.  
    2.     /**
    3.      * Карта атрибутов модели.
    4.      * Перечисляются в дочерних классах в виде массивов следующего содержания:
    5.      *
    6.      * 'first_name' => array('db_element' => true,
    7.      *                       'db_field_name' => 'user_first_name',
    8.      *                       'default_value' => null,
    9.      *                       'validators' => array(
    10.      *                           'Common/Decimal' => array('unsigned' => false), ...)
    11.      *                       )
    12.      *
    13.      * Допустимые свойства и их возможные значения:
    14.      *
    15.      * type        Тип данных свойства. Тип указывается только для сложных, не скалярных типов,
    16.      *               например, для таких, как объект Module_Common_Type_Datetime,
    17.      *               Module_Common_Type_Email и т.д. Если тип не указан, значит, это скаляр.
    18.      * db_element    [true|false] должно ли это свойство записываться в БД.
    19.      *               В большинстве случаев это свойство устанавливается в true. Исключения составляют
    20.      *               какие-то "вспомогательные" свойства объекта, которые допустимо иметь в качестве
    21.      *               членов класса, но записывать в БД не нужно.
    22.      *               Например, свойство ID Primary Key для каждой таблицы имеет значение false по
    23.      *               причине того, что никогда не пишется в таблицу, а является лишь указателем
    24.      *               на запись, т.е. фактически является "вспомогательным" в данной терминологии.
    25.      * db_field_name Имя поля таблицы данных, ассоциируемое с данным свойством класса.
    26.      * default_value Значение свойства по умолчанию, при инстанцировании объекта.
    27.      *               Данный параметр никак не связан со значением DEFAULT SQL-описания таблицы данных.
    28.      * validators    Массив валидаторов, которые должны быть применены к свойству при
    29.      *               присвоении ему значения.
    30.      *
    31.      * @var array
    32.      */
    33.  
    ну и модели выглядят так:
    PHP:
    1.  
    2. <?php
    3. class Module_User_Model_User extends Base_Model
    4. {
    5.     protected static $db_field_prefix = 'user';
    6.  
    7.     protected static $model_attributes = array
    8.     (
    9.         'id' => array('db_element'=>FALSE,
    10.                       'default_value' => 0,
    11.                       'validators' => array(
    12.                           'Common/Decimal' => array('unsigned' => false),
    13.                       )
    14.                      ),
    15.  
    16.         'active' => array('db_element'=>TRUE,
    17.                           'db_field_name' => 'user_active',
    18.                           'default_value' => 1,
    19.                           'validators' => array(
    20.                               'Common/EmptyNull' => array(),
    21.                               'Common/Decimal' => array('unsigned' => true),
    22.                               'Common/IntRange' => array('min' => 0, 'max' => 1),
    23.                           )
    24.                          ),
    25.  
    26.         'group'  => array('db_element'=>TRUE,
    27.                           'db_field_name' => 'user_group',
    28.                           'default_value'=> 2, // 2 - ID группы Пользователи
    29.                           'validators' => array(
    30.                               'Common/Empty' => array(),
    31.                               'Common/Decimal' => array('unsigned' => true),
    32.                           )
    33.                          ),
    34.  
    35.         'login'  => array('db_element'=>TRUE,
    36.                           'db_field_name' => 'user_login',
    37.  
    38.                           'validators' => array(
    39.                               'Common/EmptyNull' => array(),
    40.                               'Common/StringLength' => array(
    41.                                   'start' => 0,
    42.                                   'stop' => Module_Common_Validator_StringLength::VARCHAR_MAX_LENGTH),
    43.                               'Common/CharPassword' => array(),
    44.                           )
    45.                          ),
    46.  
    47.         'mail'   => array('type' => 'Module_Common_Type_Email',
    48.                           'db_element' => TRUE,
    49.                           'default_value' => null,
    50.                           'db_field_name' => 'user_mail',
    51.                           'validators' => array(
    52.                               'Common/StringLength' => array(
    53.                                   'start' => 0,
    54.                                   'stop' => Module_Common_Validator_StringLength::VARCHAR_MAX_LENGTH),
    55.                               'Common/Email' => array(),
    56.                           )
    57.                          ),
    58.  
    59.         'password' => array('db_element'=>TRUE,
    60.                             'db_field_name' => 'user_password',
    61.                             'validators' => array(
    62.                                 'Common/CharPassword' => array(),
    63.                             )
    64.                            ),
    65.  
    66.         'regdate'  => array('type' => 'Module_Common_Type_Datetime',
    67.                             'db_element'=>TRUE,
    68.                             'db_field_name' => 'user_regdate',
    69.                             'default_value' => 'now',
    70.                             'validators' => array(
    71.  
    72.                             )
    73.                            ),
    74.  
    75.         'visitdate' => array('type' => 'Module_Common_Type_Datetime',
    76.                              'db_element'=>TRUE,
    77.                              'db_field_name' => 'user_visitdate',
    78.                              'default_value'=>null,
    79.                              'validators' => array(
    80.  
    81.                              )
    82.                             ),
    83.  
    84.         'ip'     => array('db_element'=>TRUE,
    85.                           'db_field_name' => 'user_ip',
    86.                           'default_value'=>null,
    87.                           'validators' => array(
    88.                               //'Common/FixedStringLength' => array(15),
    89.                           )
    90.                          ),
    91.  
    92.         'first_name' => array('db_element'=>TRUE,
    93.                               'db_field_name' => 'user_first_name',
    94.                               'default_value'=>null,
    95.                               'validators' => array(
    96.                                   'Common/StringLength' => array('start' => 0, 'stop' => 30),
    97.                               )
    98.                              ),
    99.  
    100.         'last_name' => array('db_element'=>TRUE,
    101.                              'db_field_name' => 'user_last_name',
    102.                              'default_value'=>null,
    103.                              'validators' => array(
    104.                                  'Common/StringLength' => array('start' => 0, 'stop' => 30),
    105.                              )
    106.                             ),
    107.  
    108.         'age' => array('type' => 'Module_Common_Type_Datetime',
    109.                        'db_element'=>TRUE,
    110.                        'db_field_name' => 'user_age',
    111.                        'default_value'=>null,
    112.                        'validators' => array(
    113.  
    114.                        )
    115.                       ),
    116.  
    117.         'sex' => array('db_element'=>TRUE,
    118.                        'db_field_name' => 'user_sex',
    119.                        'default_value'=>null,
    120.                        'validators' => array(
    121.                            'Common/StringLength' => array('start' => 1, 'stop' => 1),
    122.                            'Common/VarEnum' => array('enum' => array('M', 'F')),
    123.                        )
    124.                       ),
    125.  
    126.         'city' => array('db_element'=>TRUE,
    127.                         'db_field_name' => 'user_city',
    128.                         'default_value' => 0,
    129.                         'validators' => array(
    130.                             'Common/Decimal' => array('unsigned' => true),
    131.                         )
    132.                        ),
    133.  
    134.         'region' => array('db_element'=>TRUE,
    135.                           'db_field_name' => 'user_region',
    136.                           'default_value' => 0,
    137.                           'validators' => array(
    138.                               'Common/Decimal' => array('unsigned' => true),
    139.                           )
    140.                          ),
    141.  
    142.         'country' => array('db_element'=>TRUE,
    143.                            'db_field_name' => 'user_country',
    144.                            'default_value' => 0,
    145.                            'validators' => array(
    146.                                'Common/Decimal' => array('unsigned' => true),
    147.                            )
    148.                           ),
    149.  
    150.         'phone' => array('db_element'=>TRUE,
    151.                          'db_field_name' => 'user_phone',
    152.                          'default_value'=>null,
    153.                          'validators' => array(
    154.                              'Common/StringLength' => array(
    155.                                  'start'=> 0,
    156.                                  'stop' => Module_Common_Validator_StringLength::VARCHAR_MAX_LENGTH),
    157.                          )
    158.                         ),
    159.  
    160.         'icq' => array('db_element'=>TRUE,
    161.                        'db_field_name' => 'user_icq',
    162.                        'default_value'=>null,
    163.                        'validators' => array(
    164.                            'Common/Decimal' => array('unsigned' => true),
    165.                            'Common/IntRange' => array(
    166.                                'min' => 10000,
    167.                                'max' => Module_Common_Validator_IntRange::PHP_MAX_INT_32)
    168.                        )
    169.                       ),
    170.  
    171.         'url' => array('db_element'=>TRUE,
    172.                        'db_field_name' => 'user_url',
    173.                        'default_value' => null,
    174.                        'validators' => array(
    175.                            'Common/StringLength' => array(
    176.                                'start' => 0,
    177.                                'stop' => Module_Common_Validator_StringLength::VARCHAR_MAX_LENGTH),
    178.                            'Common/Url' => array()
    179.                        )
    180.                       ),
    181.     );
    182.  
    183.     /**
    184.      * TRUE, если пользователь принадлежит к группе "пользователи"
    185.      *
    186.      * @var boolean
    187.      */
    188.     protected $is_user;
    189.  
    190.     /**
    191.      * TRUE, если пользователь принадлежит к группе "администраторы"
    192.      *
    193.      * @var boolean
    194.      */
    195.     protected $is_administrator;
    196.  
    197.     public function isGuest()
    198.     {
    199.         // У гостей ID модели User всегда меньше 0 (-1)
    200.         // поэтому лезть в базу не обязательно
    201.         return $this->getId() < 0;
    202.     }
    203.  
    204.     public function isUser()
    205.     {
    206.         if ($this->is_user === null)
    207.         {
    208.             $Module_Group_Mapper_Group = new Module_Group_Mapper_Group();
    209.             $group = $Module_Group_Mapper_Group->findGroupByAlias('user');
    210.  
    211.             $this->is_user = $this->getGroup() == $group->getId();
    212.         }
    213.  
    214.         return $this->is_user;
    215.     }
    216.  
    217.     public function isAdministrator()
    218.     {
    219.         if ($this->is_administrator === null)
    220.         {
    221.             $Module_Group_Mapper_Group = new Module_Group_Mapper_Group();
    222.             $group = $Module_Group_Mapper_Group->findGroupByAlias('administrator');
    223.  
    224.             $this->is_administrator = $this->getGroup() == $group->getId();
    225.         }
    226.  
    227.         return $this->is_administrator;
    228.     }
    229.  
    230.     public function getFullName()
    231.     {
    232.         return $this->first_name.
    233.                ($this->last_name ? ' '.$this->last_name : '');
    234.     }
    235.  
    236.     public function getFullNameOrLogin()
    237.     {
    238.         return $this->getFullName()
    239.                ? $this->getFullName()
    240.                : $this->getLogin();
    241.     }
    242.  
    243.     public function getAgeDay()
    244.     {
    245.         if ($this->age && $this->age instanceof Module_Common_Type_Datetime)
    246.         {
    247.             return $this->age->format('j');
    248.         }
    249.  
    250.         return null;
    251.     }
    252.  
    253.     public function getAgeMonth()
    254.     {
    255.         if ($this->age && $this->age instanceof Module_Common_Type_Datetime)
    256.         {
    257.             return $this->age->format('n');
    258.         }
    259.  
    260.         return null;
    261.     }
    262.  
    263.     public function getAgeYear()
    264.     {
    265.         if ($this->age && $this->age instanceof Module_Common_Type_Datetime)
    266.         {
    267.             return $this->age->format('Y');
    268.         }
    269.  
    270.         return null;
    271.     }
    272.  
    273.     /**
    274.      * @see parent::getId()
    275.      */
    276.     public function setId($id)
    277.     {
    278.         if (!empty($this->data['id']) && $this->data['id'] != -1 && $this->data['id'] != $id)
    279.         {
    280.             throw new LogicException('Нельзя переопределить значение ID объекта модели '.
    281.                                      get_class($this));
    282.         }
    283.  
    284.         $this->id = $id;
    285.  
    286.         return true;
    287.     }
    288.  
    289.     protected function _setUrl($url)
    290.     {
    291.         return $url === 'http://' ? null : $url;
    292.     }
    293.  
    294.     protected function _setPassword($password)
    295.     {
    296.         if ($password === null || $password === '')
    297.         {
    298.             return null;
    299.         }
    300.  
    301.         return md5($password);
    302.     }
    303. }
    304. ?>
    305.  
     
  19. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Духовность
    интересная идея, а что в самом маппере? как вызываются и возвращают ошибки валидаторы?
    проблема памяти не вставала? я думал сделать регистр моделей, что бы по 10 раз один и тот же объект не создавать
    над Lazy Load коллекций не думал?пока не нашел где мне это надо, но если удаляем допустим юзера я бы не отказался если б за ним удалялось все остальное
     
  20. Духовность

    Духовность Активный пользователь

    С нами с:
    1 дек 2010
    Сообщения:
    4
    Симпатии:
    0
    концептуальных отличий от твоего меппера нет. принцип тот же. остальное - тонкости архитектуры, которые смысла описывать нет.

    при присвоении свойства объекту модели происходит валидация. Валидация в модели носит лишь уведомительный характер. Принимать решение, что делать с моделью с ошибками должен слой, оперирующий с этой моделью (контроллер/меппер):

    PHP:
    1.  
    2. $user = new User();
    3. $user->setLogin(''); // возникла ошибка, но свойство присвоилось
    4.  
    5. if (!$user->getErrors()) {
    6.     $mapper->add($user);
    7. }
    8. else {
    9.     echo 'Ошибка!';
    10. }
    11.  
    В общем, это тонкости все, которые накладываются на уже существующую архитектуру и существующие по историческим причинам.

    Я считаю своим достижением в данном решении то, что я сделал не просто сухой меппинг табличных данных, а реализовал ситуацию, когда каждое свойство класса может быть либо скаляром (что нужно в 90% случаев), либо объектом какого-то ТИПА - объектом Datetime, Email и т.д:

    PHP:
    1.  
    2. $user = Mapper::find(1);
    3.  
    4. $user->getVisitdate() // возвращает объект Module_Common_Type_Datetime который extends Datetime
    5.         ->getDateAsStrinп(); // Сегодня, 12:23
    6. $user->getMail()->getDomain(); // mail.ru
    7.  
     
  21. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    PHP:
    1. <?php
    2. $user = Mapper::find(1);
    3. $user->getVisitdate() // возвращает объект Module_Common_Type_Datetime который extends Datetime
    4.         ->getDateAsStrinп(); // Сегодня, 12:23
    5. $user->getMail()->getDomain(); // mail.ru
    а вот про это я и спрашивал
    реализацию Мэтта Зандстра видел? есть на что посмотреть
    [​IMG]
     
  22. Духовность

    Духовность Активный пользователь

    С нами с:
    1 дек 2010
    Сообщения:
    4
    Симпатии:
    0
    ты про книгу? мне книга очень не понравилась - не читабельная она какая-то, воды очень много. Фаулер хоть и сложнее пишет, но доходит больше :)
     
  23. Padaboo

    Padaboo Старожил
    Команда форума Модератор

    С нами с:
    26 окт 2009
    Сообщения:
    5.242
    Симпатии:
    1
    Духовность
    с Фаулером не сравнить конечно, но по php я лучше не встречал по крайней мере, странно, может издание не то?
    Там совсем другая реализация, которая скорее ближе к такому варианту:
    http://phpdatamapper.com/
    но там тоже свои плюсы