Как лучше организовать проект (посоветуйте)
Проблема у меня выросла недавно в виду моей лютой любви к абстрагированости, посему возникла проблема в целесообразности и разумности придуманного.
Описание
Пусть будет довольно-таки большой сайт (пока в теории, при хорошем раскладе на практике), в котором будет 30-45 таблиц, сплошное ООП. Допустим, есть исполнитель (музыкальный), у него есть альбомы, песни, список гастролей и т.д. Исполнитель, альбом, песни, пр. - все представляется в системе объектами. Проблема в том, как лучше организовать загрузку данных в эти классы.
Например, есть главная страница, а не ней надо будет вывести всю инфу исполнителя, пару альбомов, десяток случайных песен. Реализовать это можно двумя методами (на мой взгляд):
1) Первый метод (дедовский, быстрый, но не гибкий).
Здесь просто в странице по адресу artist_info.php?id=<id> имеем:
Код:
$rs = $DB->query("SELECT <fields> FROM `artists` WHERE id=...");
// далее обработка опроса, заполнение объекта, обработка исключений, ошибок, прочего
$rs = $DB->query("SELECT <album_fields> FROM `albums` WHERE ...");
// то же самое - обработка ошибок и прочее
...
// то же самое для песен и прочего
...
// тут работает шаблонизатор и вывод
// далее обработка опроса, заполнение объекта, обработка исключений, ошибок, прочего
$rs = $DB->query("SELECT <album_fields> FROM `albums` WHERE ...");
// то же самое - обработка ошибок и прочее
...
// то же самое для песен и прочего
...
// тут работает шаблонизатор и вывод
2) Второй метод, который хочу использовать я.
В классе исполнителя описываются все его атрибуты (например: "title", "created", "comments_count", "albums"). Для загрузки данных требуется лишь указать атрибуты, а уж вся нагрузка на генерацию SQL и обработку результатов ложится на функцию, делающую поиск (например, ArtistUtils::getById($id, array $props).
Код:
// artist_info.php
$Artist = ArtistUtils::getInstance()->getById(
<id>,
array(
"id", "title", "is_online", "info", "albums", "comments_count", "tours"
)
);
// в самом классе существует массив, отражающий атрибут объекта
// в его SQL-представление, т.е. типа:
$prop_to_sql = array(
"id" => "ar_id AS id",
"title" => "ar_title AS title",
"is_online" => "((NOW()-last_visit) < 100500) AS is_online",
....
);
$Artist = ArtistUtils::getInstance()->getById(
<id>,
array(
"id", "title", "is_online", "info", "albums", "comments_count", "tours"
)
);
// в самом классе существует массив, отражающий атрибут объекта
// в его SQL-представление, т.е. типа:
$prop_to_sql = array(
"id" => "ar_id AS id",
"title" => "ar_title AS title",
"is_online" => "((NOW()-last_visit) < 100500) AS is_online",
....
);
Все sql-выражения для comments_count и подобных будут браться из классов типа Comment, Tour, прочее.
При запросе getById будут отдельно обрабатываться "albums", т.е. при установке этого флага будет автоматически производится запрос к albums и упаковывание объекта данными.
Плюс первого метода - это быстрая разработка, более быстрое время исполнения, по сравнению со вторым методом.
Минусы первого метода - при добавлении какого-либо нового атрибута в класс, придется вручную везде вписывать значения в SQL-запрос во всех SQL-ах в проекте, где потребуется вывод данного атрибута. А также самому обрабатывать данные, исправлять что-то.
Плюсы второго метода - абстракция от БД, все работы по выборкам и генерации SQL содержатся лишь в одном месте, легче организовать кэширование объектов (дабы по 100 раз не дергать БД).
Простота добавления новых атрибутов, визуальная легкость кода при получении требуемых атрибутов.
Легкость получения новых данных (легче в массиве перечислить что надо + в объекте будет перечень всех доступных атрибутов, в то время как в первом методе надо каждый раз самому следить за их установкой).
Минусы - значительно большее время разработки и время выполнения, большее поедание памяти и код может быть не понят с первого раза (ибо мб запутанность) посторонним.
Хорош ли мой подход? Что лучше? Или есть альтернативные варианты и решения?
В таких случаях каждый решает сам. Можно и без ООП разработать проект, где добавлять новые атрибуты и свойства будет ничуть не сложнее.
Если это простой случай выборки по ID, то в классе драйвера таблицы MySQL есть функция типа SelectBy('id', $id, 'field'), где 'field' также может быть массивом array('field1', 'field2', ...). Здесь лишних классов не создаётся, т.к. класс таблицы может быть использован где угодно.
Если это более сложный вариант, типа рандомной записи, самой популярной и т.д., то делаю класс, например, для песен, где выполняются эти запросы. А из кода обращаюсь к этому классу-обёртке.
С кешированием немного по-другому, но не сложнее. Так как я всё равно делаю класс для песен (см. предыдущий параграф), то туда же вписываю методы, которые получают инфу о песне и одновременно её кешируют. Просто:
Код:
class Song {
static $fields = array('id', 'title', 'is_online', 'info', 'albums', 'comments_count', ...);
static $cache = array();
static function InfoAbout($id, $field) {
if (!isset( self::$cached[$id] )) {
self::$cached[$id] = Table('songs')->SelectBy('id', $id, self::$fields);
}
return self::$cached[$id][$field];
}
static $fields = array('id', 'title', 'is_online', 'info', 'albums', 'comments_count', ...);
static $cache = array();
static function InfoAbout($id, $field) {
if (!isset( self::$cached[$id] )) {
self::$cached[$id] = Table('songs')->SelectBy('id', $id, self::$fields);
}
return self::$cached[$id][$field];
}
Таким образом получаем одновременно прозрачное кеширование и доступ к полям песни.
О кешировании за пределами этого метода можем не думать. Ничто не мешает сделать так, не заботясь о том, что в БД уйдут два одинаковых запроса:
Код:
<title>Песня "<?=Song::InfoAbout($id, 'title')?>" - <?=Song::InfoAbout($id, 'author')?></title>
У меня тут как бы другой вопрос - в самом подходе к задаче. Т.е. не в том, как кэшировать и как обрабатывать результат, а в том, как вообще организовать лучше и разумнее стрктуру хранения и получения (+ рассмотреть удобство пользования и рефакторинга).
Короче, есть класс интерфейса таблицы для мускула, есть класс песни, который использщует эту таблицу и выступает как буфер между БД и внешним кодом.
Характеристики песни в поля такого класса я бы записывать не стал, использовать обычный массив проще и обычно хватает.
Сначала решите что вам нужно: производительность или красота кода. Ответ на этот вопрос и будет ответом на вопрос как организовать проект.
этой)? :)
Неужели для PHP нету нормальных ORM и/или генераторов прослойки уровня доступа к данным (подобным
PHP это скриптовый язык. Некомпилируемый даже в байткод (в отличие от Java, Action Script и т.д.). А вы на него навешиваете такие тяжелые хрени. Если проект маленький или это интранет сайт - то там все равно. А если проект крупный, то подобные штуки выйдут вам боком.
Цитата: RussianSpy
PHP это скриптовый язык. Некомпилируемый даже в байткод (в отличие от Java, Action Script и т.д.). А вы на него навешиваете такие тяжелые хрени. Если проект маленький или это интранет сайт - то там все равно. А если проект крупный, то подобные штуки выйдут вам боком.
подобные утверждение актуально было бы лет 5 назад для PHP4. В настоящее время использование ООП-решений в PHP вполне оправдано.
Тем более, что PHP компилируется во время выполнения в байт-код и расширения для PHP, которые его кешируют (типа XCache) широко распространены.
Цитата: Proger_XP
Тем более, что PHP компилируется во время выполнения в байт-код и расширения для PHP, которые его кешируют (типа XCache) широко распространены.
Естественно. Только далеко не на каждом хостинге они присутствуют. Однако если проект крупный, то и на хостинг его сажать не будут.
В общем дабы не разводить флуд обозначу четко свое мнение: гибридный вариант без высокоабстрактной логики.
Тут все зависит от ТС во многом. Мое ИМХО - создание ООП-модели в любом случае лишним не будет - даже если сам по себе код не будет является православно-ООП :) - страшного в єтом нет ничего. Говорить о том, какой путь лучше можно тогда, когда есть понимание, есть модель объектов, модель состояний и т.п. - тогда и будет понятно - нужна высокоабстрактная логика ли тут.
Вообщем, от "высокоуровневой" абстракции над данными решил отказаться по следующим причинам (чем первее пункт, тем важнее для меня):
1) Гибкости, как таковой, особо не получается. Да, я получаю простоту доступа к данным. Но вот операции над ними уже не становятся такими гибкими, слишком много возни и вычислительных мощностей требуется для реализации простых действий. Вообщем, тут сложно объяснить, надо просто масштаб кода и затеи видеть.
2) Объем кода. Начав делать я понял, что писать для всех (в теории) 20-25 разных сущностей код такой абстракции - можно повеситься.
3) Время выполнения скрипта сильно возрастает, что мне не подходит
Попрограммировав за это время на других языках и набравшись немного мировоззрения, я понял, что не следует страдать "херней", потому что php такого издевательства не потерпит и не потянет.
В итоге пойду старым добрым методом: описание сущностей содержится в классе (аля-struct с некоторыми вспом.методами) + общие статические классы, содержащие базовые общие операции над сущностями, типа "создать", "редактировать" и т.д. А все нагрузку за заполнение сущностей данными и прочим вынести из классов (как хотел раньше) и заполнять в ручную в зависимости от потребностей в текущей точке коде.
Ибо с пхп воротить задуманное - повеситься можно. На там же Nemerle хотя бы макросы помогли бы сократить объемы, но увы.
[COLOR="LightGray"]По-моему, пора создавать группу "Верующие в макросы Nemerle".[/COLOR]