Итак, почему и в пользу чего, я отказался от ActiveRecord.
Для начала - Почему!
Собственно я кривлю душой, говоря, что я не использую AR, да в моейм ФВ он есть, но я потихонечку от него отказываюсь. Есть он у меня в таком виде:
Что есть что, просты типы, думаю понятны, VIRTUAL - это тип сохраняет и восстанавливает из поля blob->array. При этом получается вот такая штука
.
RS - сокращенно от RecordsSet. Сложные типы данных VIRTUAL, RS, OBJ - реализуют паттерн Lazy Initialization.
Впринципе для более менее простых таблиц очень удобно. Почему отказываюсь, потому-как большие объемы и разнородность вытягиваемых данных с которыми приходится работать, не поощрают описывание полей до момента их вытягивания.
Резюмируя, для чего может быть полезен паттерн, для более менее статических запросов (User, Client и т.д.), для запросов, когда из одной таблицы вытягиваются разные поля или необходимы сложные JOIN-ы, паттерн использовать можно, но в моем случае не целесообразно.
Я опущу описание статических методов SqlObject::Object, SqlObject::Recordset (один получает объект, второй массив объектов).
Дальнейшее развитие, паттерна привело к перегрузке магического метода __get, __set. В которых начала появляться более сложная логика получения данных привязанных к объекту.
Как-я и писал, на определенном этапе, меня перестало это устраивать, описание полей + некоторые аспекты повышения производительности кода, натолкнули на решение отказаться от AR в чистом виде и появился некий симбиоз
Что получилось!
Теперь я использую прямые запросы к БД, минуя QueryBuilder-ы. Выглядит это примерно так
Что мне это дало.
- Гибкость - теперь можно не используя сложное наследование и сложные описание извлекаемых (обновляемых) данных, использовать прелести AR. При этом сохранилась основная возможность LazyInitialization + Lazy Load
- Увеличилась производительность (в основном за счет отсутсвия инициализации полей + отсутствия query builder-а)
- Добавилась некоторая удобная функциональность, теперь к примеру можно использовать переменную класса в таком виде
- Появилась возможность контролируемо JOIN-нить поля (в основном это относится к оптимизации индексов, по которым выбираются данные)
- Прозрачность и понятность запроса, что позволяет точно знать какой именно запрос выполняется + возможность его оптимизации и отладки (EXPLAIN, EXPLAIN EXTENDED)
Что потеряли:
- Да, теперь запросы, даже примитивные придется писать вручную, впрочем, как дабавление и обновление данных.
- Из чего вытекает более жесткий контроль за данным (помним про sql инъекции).
Где это имеет место быть?
Основное, это когда идут большие объемы данных, где надо четко контролировать запрос, где есть сложные JOIN-ы.
Вот собственно два подхода и мое обоснование, почему я ухожу от AR в чистом виде.
Для начала - Почему!
Собственно я кривлю душой, говоря, что я не использую AR, да в моейм ФВ он есть, но я потихонечку от него отказываюсь. Есть он у меня в таком виде:
PHP:
class Client extends SqlObject
{
public function __construct() {
parent::__construct('clients_schema');
$this->INT('id')->INT('cl_ownership',0,'ownership')->VARCHAR('cl_name', 256, 'name')->INT('cl_state',1,'state')->
VARCHAR('cl_contact_name', 128, 'contact_name')->PHONE('cl_contact_phone','contact_phone')->
VARCHAR('cl_contact_email', 64, 'contact_email')->INT('cl_contact_post', 3, 'contact_post')->INT('cl_contact_access', 0, 'contact_access')->
VARCHAR('cl_license', 64, 'document')->
PHONE('cl_fax','contact_fax')->PHONE('cl_phone','phone')->
DATE('cl_date_create','date_create')->INT('cl_manager',0,'manager_id')->
VIRTUAL('cl_address_post', array('country'=>32,'city'=>48,'region'=>64,'area'=>64,'address'=>256,'zip'=>24),'address_post')->
VIRTUAL('cl_address_office', array('country'=>32,'city'=>48,'region'=>64,'area'=>64,'address'=>256,'zip'=>24),'address_office')->
VIRTUAL('cl_account_details', array('number'=>32,'unn'=>32,'okpo'=>32,'ru_number'=>40,'ru_bik'=>40,'ru_inn'=>40,'ru_cor'=>40,'ru_kpp'=>40,'ru_okpo'=>40,'ru_ogrn'=>40), 'account_details')->
VIRTUAL('cl_bank', array('name'=>128,'suboffice'=>128,'code'=>16,'location'=>'128'), 'bank')->
VIRTUAL('cl_passport', array('number'=>32,'pin'=>64,'date'=>16,'issued'=>'128'), 'passport')->
VIRTUAL('cl_ip', array('license'=>64,'date'=>16), 'ip')->INT('cl_owner',1,'company')->
RS('workflow','id','client_id','Workflow');
}
PHP:
$Object->address_post['country']
RS - сокращенно от RecordsSet. Сложные типы данных VIRTUAL, RS, OBJ - реализуют паттерн Lazy Initialization.
Впринципе для более менее простых таблиц очень удобно. Почему отказываюсь, потому-как большие объемы и разнородность вытягиваемых данных с которыми приходится работать, не поощрают описывание полей до момента их вытягивания.
Резюмируя, для чего может быть полезен паттерн, для более менее статических запросов (User, Client и т.д.), для запросов, когда из одной таблицы вытягиваются разные поля или необходимы сложные JOIN-ы, паттерн использовать можно, но в моем случае не целесообразно.
Я опущу описание статических методов SqlObject::Object, SqlObject::Recordset (один получает объект, второй массив объектов).
Дальнейшее развитие, паттерна привело к перегрузке магического метода __get, __set. В которых начала появляться более сложная логика получения данных привязанных к объекту.
Как-я и писал, на определенном этапе, меня перестало это устраивать, описание полей + некоторые аспекты повышения производительности кода, натолкнули на решение отказаться от AR в чистом виде и появился некий симбиоз
Что получилось!
Теперь я использую прямые запросы к БД, минуя QueryBuilder-ы. Выглядит это примерно так
PHP:
class Client extends TObject
{
protected function address_post()
{
return unserialize('cl_address_post');
}
public function workflow($fromDate=false,$toDate=false)
{
return sql::recordset(sql::query("SELECT * FROM ... WHERE client_id=%d",$this->id),false,'Workflow');
}
static public function Get($ClientId)
{
return sql::object(sql::query("SELECT id,cl_name as `name`, .... cl_address_post FROM clients_schema WHERE id=%d",$ClientId),'Client');
}
static public function Update($DataSet,$ClientId=false) { ... }
}
- Гибкость - теперь можно не используя сложное наследование и сложные описание извлекаемых (обновляемых) данных, использовать прелести AR. При этом сохранилась основная возможность LazyInitialization + Lazy Load
- Увеличилась производительность (в основном за счет отсутсвия инициализации полей + отсутствия query builder-а)
- Добавилась некоторая удобная функциональность, теперь к примеру можно использовать переменную класса в таком виде
PHP:
$Object->Workflow;
# Либо расширенный функционал
$Object->Wokflow($fromDate,$toDate);
- Прозрачность и понятность запроса, что позволяет точно знать какой именно запрос выполняется + возможность его оптимизации и отладки (EXPLAIN, EXPLAIN EXTENDED)
Что потеряли:
- Да, теперь запросы, даже примитивные придется писать вручную, впрочем, как дабавление и обновление данных.
- Из чего вытекает более жесткий контроль за данным (помним про sql инъекции).
Где это имеет место быть?
Основное, это когда идут большие объемы данных, где надо четко контролировать запрос, где есть сложные JOIN-ы.
Вот собственно два подхода и мое обоснование, почему я ухожу от AR в чистом виде.