Посоветуйте как организовать код

arturabramian

Php, база в mysql. В базе в разных таблицах хранятся объекты, сейчас мне нужно напрогать всяких функций, чтобы один из этих объектов разными способами выводить на сайт. Конкретнее, нужный мне объект - это игра (спортивная). В базе хранятся следующие данные об игре:
турнир
время проведения
команды (названия, точнее их id)
счёт
составы команд (вынесены в отдельную таблицу, связывающую id игры с id принимавших участие игроков)
судьи
и т.д.
При этом вся инфа в разных таблицах, связь между таблицами по id. То есть, чтобы узнать фамилию судьи, нужно сначала из таблицы с играми по id игры определить id судьи, а потом из таблицы с судьями узнать его фамилию.
В разных местах сайта нужно выводить разную информацию об игре. Здесь - турнир и названия команд, там - составы, ещё где-нибудь - счёт и дату, и т.п.
Все нужные функции и запросы я в состоянии написать, но у меня есть сомнения в том, что я выбираю оптимальный способ организации кода. Сейчас я думаю делать так: пишем класс game, объявляем в нём дохрена переменных, в которых будет храниться вся-вся информация об игре, собранная из разных таблиц БД, какая только может потребоваться. Пишем разные функции, которые будут из этих переменных составлять нужные мне строковые переменные, чтобы я их засовывал на сайт куда надо.
Вопрос в том, как заполнять переменные с информацией об игре. Можно на каждую группу переменных писать свою функцию, заполняющую данные из базы:
function get_players{
// query to get players having game id
...
// fill the $players array with this data
...
}
function get_teams{...}
function get_score{...}
и т.д. В этом случае, если мне потребуется вывести в одном блоке всю информацию об игре, прога сделает дохрена запросов, чтобы всю инфу последовательно из базы выцепить. Я примерно посчитал, получается что сейчас всю (вообще всю) инфу об игре можно вытащить из базы пятью запросами, а при таком подходе, когда мы по кусочкам вытягиваем, общее количество запросов будет порядка двадцати.
Другой вариант - это с самого начала, где-нибудь ещё в конструкторе, проинициализировать все переменные из базы. Т.е. сразу знать об этой игре всё что только можно. Но тогда обратная ситуация: допустим, мне в каком-то месте нужна только информация о командах, а я создаю здоровенный класс, набираю в него кучу данных- о турнире, об игроках, - а использую только малую часть из этого.
Если я буду рисовать турнирную таблицу, то потребуются данные о 50 играх. Хочется такой код, чтобы работало побыстрее. Как я понимаю, множественные запросы влияют на скорость? Или, если запросы маленькие (обрабатывается небольшое число записей, порядка десяти то на количество запросов можно забить?
Что скажут отцы пхп? Может, есть какой-то достаточно универсальный подход к тому, как организовывать хранение и обработку такого рода данных в проге, чтобы было удобно и быстро работало?

kruzer25

Делай ленивые объедки ;)

pitrik2

а в пхп есть ORM-ы ?

sinet

Propel + Creole я бы посоветовал автору.

pitrik2

а вообще если хочешь то можем встретиться в какомнить кофехауз и все обсудить
возьмем листочек/ручку, начертим как все примерно будет выглядеть
я уже так раз встречался, прикольно получилось
можно еще по аське, но это долго и токо вечером

arturabramian

А что, давай Ща приват тебе напишу.
Вообще, мне не хватает навыка "хорошего программирования". Как я понимаю, это достигается либо потом-кровью-своими ошибками, либо ценными советами какого-нибудь наставника (например, на работе либо школой (не знаю, преподают ли такое на ВМК? или у нас на вычмате? подозреваю, что нет).
Наверняка ведь есть какие-то прописные истины, которые всем опытным программистом известны? Лично я пока только до одной допёр: "написал одинаковый код три раза - пиши функцию, использовал одинаковую константу три раза - объявляй специальную переменную, и т.п."

kruzer25

Например, можешь почитать фаулера ;)

lubanj

в таком случае тебе нужно для начала почитать "Совершенный код" Макконнелла
а потом уже вопросами общей архитектуры кода заниматься.
зы: даже если используешься константу больше НУЛЯ раз в программе, ее следует выносить

arturabramian

Ну, есть такие "естественные" константы, как 7 или 12 (дни недели, месяцы). Их, я думаю, выносить не надо. А так вообще - да, я понимаю что так правильно, но это ведь надо будет где-то специально их всех объявлять, и помнить, какая как называется (или смотреть каждый раз).
Книжку почитаю, спасибо. Возможно, это как раз то что мне нужно.

arturabramian

Эммм... я, конечно, понимаю, что ты типа хочешь мне помочь, и спасибо конечно, но помощь твоя весьма странную форму принимает. Совершенно неясно, что ты имеешь в виду.
Моя знать как искать инфа в интернет, однако Фаулеров много, и даже у Мартина Фаулера есть разные книги. И "экстремальное программирование", если ты именно про него, не один Фаулер написал.

kruzer25

У мартина фаулера ещё книжка про архитектуру есть ;)

kruzer25

Ну, есть такие "естественные" константы, как 7 или 12 (дни недели, месяцы). Их, я думаю, выносить не надо.
Представляешь, их тоже надо выносить.
Когда в коде где-то там встречается какое-то магическое число 7 - совершенно непонятно, что оно означает, что оно здесь делает, откуда оно взялось. А вот если там написано WEEK_IN_DAYS - то всё сразу становится понятно.

okunek

и помнить
ничего страшного, память заодно потренируешь :)

lubanj

----------------------------------------------------------
тут был пост о мега крутой среде разработки

okunek

Cотри срочно! :grin:

lubanj

:grin:
вот путь джедая:
сначала тренировка памяти, потом прога на несколько тысяч строк, потом макконнелл и уж после всего этого ИДЕ

slonishka

лучше б про то, как вы в акронисе делаете охуенные приложения рассказал,
чем на говномакконела дрочить. :)

slonishka

а то блин, как ни зайдешь в девелопмент, обсуждают уровни абстракции и объектно-ориентированный подход. :(

okunek

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

livemix

Могу написать несколько конкретных предложений по организации. Тока сначала вопросов пара есть - используется PHP5? И, это единичный проектик, или есть варианты, что и дальше придется делать что-то похожее?
Смысл второго вопроса в том, готов ли ты потратить недельку (или даже две) на разработку небольшого фреймворка, с помощью которого потом удобно и быстро клепать приложения? Просто если приложение надо написать одно и небольшое, то писать ради этого фреймворк - дело неоправданное с точки зрения времени, и, как следствие, финансов.

sinet

Зачем писать свое, если можно взять готовое? :)
Даже чтобы самому разобраться, проще посмотреть, как сделано у других.

livemix

Свое проще тюнить. И свое неуниверсальное может оказаться лучше, чем не свое универсальное.

sinet

Ну фиг знает. Propel я без проблем тюнил, чтобы он с ораклом лучше дружил, а для мускуля его за глаза хватит.

livemix

А если я хочу такое:
Например, я получаю количество сотрудников, работающих в данный момент в компании. Оно меняется только при добавлении/удалении сотрудника. Поэтому логично положить результаты запроса в кэш (xCache, Apc) и т.п., причем так, чтобы при добавлении/удалении сотрудника данные в кэше стали не валидными. И хочется это не писать кастомно для данного параметра кэша, а как-то "зашить" на уровне фреймворка.
Можно легко затюнить Propel?

livemix

А вообще, все зависит от поставленных целей конечно. Например, для разработки клиентской javascript-части мы используем библиотечку jQuery. Конечно, дописываем кое-что, но писать свое не стали. Не возникло потребности. А вот для ORM решили свой сделать. Не на базе другого даже, а просто свой. Просто хочу сказать, что я не являюсь принципиальным противником готовых фреймворков

arturabramian

Могу написать несколько конкретных предложений по организации. Тока сначала вопросов пара есть - используется PHP5? И, это единичный проектик, или есть варианты, что и дальше придется делать что-то похожее?
Напиши свои предложения, пожалуйста :)
php5, наверное, не используется. Я стараюсь писать как можно проще и универсальнее.
Проект - не совсем единичный. В будущем предполагается дописывать какие-то модули.
Конкретно могу такой пример привести: бывают круговые турниры, а бывают "навылет". Сейчас нужно написать всё только для круговых, но в будущем могут потребоваться юзерские и админские интерфейсы для турниров навылет.
Насчёт фреймворков: если я правильно понимаю смысл этого слова, то я использую некоторые сторонние. Для доступа в БД, для рисования простых форм в админке, для построения навигации. Но вот для юзерских интерфейсов фактически пишу всё сам.

kruzer25

php5, наверное, не используется. Я стараюсь писать как можно проще и универсальнее.
На php5 будет и проще, и универсальнее в том смысле, что легко потом это использвоать где-то ещё.
А на php4 - универсальнее в том смысле, что есть какие-то дремучие хостеры, у которых до сих пор только php4 (поддержка которого, кстати, афаик уже прекратилась) - это всё равно, что пользоваться сегодня Windows 98.

sinet

Легкий метод - в классе каждой таблицы перекрыть doCount, doInsert, doDelete, doDeleteAll.
Сложный метод - поправить прямо в генераторе addDoCount, addDoInsert, addDoDelete, addDoDeleteAll.

sinet

А я вообще противник всяческих ORM. Слишком уж часто приходилось заставлять разработчиков использовать написанные вручную SQL-запросы, ибо генерируемые запросы тормозили. :mad:

livemix

Организуем код.
Первый хинт. В пхп5 у класса есть методы __get, __call, __set, с которым можно очень хорошо баловаться. Например, есть объект $a типа A. Если в классе A объявить метод

__get( $Name ) { return $this->data[$Name]; }

Это позволяет создать классы Game и Team, но в каждом из них не объявлять кучу полей - членов класса, а хранить все данные об игре, скажем, в массиве $data, при этом получая их удобным образом типа $game->GameName, или $game->getGameName и т.п.
Кроме того, этот функционал одинаков для классов Game и Team, т.к. он общий. Поэтому его можно вынести в абстрактного родителя - класс DbObject.
Второй хинт. Теперь надо научить класс DbObject поднимать данные из базы и складывать их в массив $data, ну и хочется также научить этот класс сохранять данные в базу.
Совсем простое решение написать статический метод в DbObject вида
 
static get( $sql, $ClassName ) {
// выполнить данный sql-запрос
// на каждую строчку полученную создать объект типа $ClassName - наследник DbObject и заполнить его массив $data данными из строчки
// вернуть массив объектов.
}

Изъян (и преимущество) в том, что придется все равно руками в коде написать sql-запрос. Иногда это выгодно, иногда рутинно и не хочется.
Можно (потребует больше усилий) создать возможность в DbObject хранить мета-данные о таблице, с которой он работает, достаточные для того, чтобы он сам формировал запрос. Но лучше не отдавать это на полную автоматику.
Третий хинт. Что делать с полями-ссылками. Сначала рассмотрим ситуацию один-ко-многим. У игры Game есть поле referee_id. Можно добиться того, чтобы метод $game->getReferee возвращал объект Referee, делая запрос в базу данных. Причем этот функционал можно реализовать в опять же в DbObject. НО. Тогда возникает проблема с отображением списка игр - ведь там нужна фамилия игры, а если доставать судей последовательно для каждой игры, то может уйти и 50 запросов в базу. Это можно решить следующим образом: написать хелпер-метод (статическую функцию, если угодно которая поднимает одним запросом судей по данным играм примерно с такой сигнатурой вызова
 fillReferenceObjects( $games, 'Referee' ) 

Опять же, функция может быть реализована полне абстрактна (там надо додумать еще параметры, конечно).
Четвертый хинт. Много-ко-многим. Продолжим пример с судьями. Судья-то в игре не один. Итак, есть таблица вида game_id, referee_id. И хочется вызывать метод $game->getReferees и получать массив объектов Referee. Поможет та же функция, что и выше, только реализация будет другая для случая много-ко-многим.
Заключение. В изложенной выше схеме еще много степеней свободы. Можно ее сделать ближе к ORM с автогенерацией запросов (но я бы так делать не стал можно просто сделать удобнее за счет грамотной организации методов. Основная цель схемы выше - вынести функционал по подъему/сохранению объектов в бд в общее место, сделать его абстрактным. Плюс добиться того, чтобы в объектах-наследника DbObject код практически был не нужен. Это избавляет тебя от кучи рутинной работы.
Да, насчет жизненности. Такую схему я сореализовывал (реализовывал в команде). Получилось удобно и работоспособно.
Поскольку все очень абстрактно, то, если есть вопросы, готов пояснить любое место.

kruzer25

Это позволяет создать классы Game и Team, но в каждом из них не объявлять кучу полей - членов класса, а хранить все данные об игре, скажем, в массиве $data, при этом получая их удобным образом типа $game->GameName
Это плохой подход - потому что допускает опечатки вроде $game->GaneName, которые будут отловлены только во время работы, если вообще будут отловлены (они же всего-навсего notice сгенерируют).
Можно (потребует больше усилий) создать возможность в DbObject хранить мета-данные о таблице, с которой он работает, достаточные для того, чтобы он сам формировал запрос.
По крайней мере, можно сделать метод, который будет грузить нужный объект по Id, во многих случаях нужно будет именно это.
А снаружи - брать нужный объект (с известным Id) из реестра.
Ну и можно тогда ещё и ленивую загрузку осуществить для таких объектов.

livemix

Это плохой подход - потому что допускает опечатки вроде $game->GaneName, которые будут отловлены только во время работы, если вообще будут отловлены (они же всего-навсего notice сгенерируют).

Я бы согласился, если бы это был не пхп, а что-нибудь компилируемое. С таким же успехом в пхп можно написать не $game, а $gane. "Волков бояться - в лес не ходить" А еще, если сначала зарегистрировать все допустимы поля (а так и надо сделать то в __get можно плевать исключение на кривое название. А вот $gane действительно только нотис дает. Так что мое предложение лучше, чем то, что предлагает нам сам пхп.

kruzer25

С таким же успехом в пхп можно написать не $game, а $gane
Если писать в блокноте.
А если писать в IDE - то хрен ты опечатаешься в имени переменной, метода или члена.
А вот $gane действительно только нотис дает
Когда ты попытаешься вызвать у него какой-нибудь метод или взять член - будет Fatal Error.

arturabramian

Спасибо, буду ботать и разбираться.

Phoenix

сейчас я функцию с такими 7 и 12 покажу..
я так и не понял, что она делает:
самое забавное, что она откуда-то мигрировала, т.к. в скриптах нигде не используется.
заметь, вполно нормальные константы.
7 - наверно дни недели
100 и 400 для определения весокосного года, наверно.
13 и 14 это 12+1 и 12+2(чтоб враги не догадались (С) )

function dow($d, $m, $y)
{
$m2 = $m;

if($m == 1)
{
$m2 = 13;
$y = $y-1;
}
if($m == 2)
{
$m2 = 14;
$y = $y-1;
}

$val4 = intval$m2+1)*3)/5);
$val5 = intval($y/4);
$val6 = intval($y/100);
$val7 = intval($y/400);
$val8 = $d+($m2*2)+$val4+$y+$val5-$val6+$val7+2;
$val9 = intval($val8/7);
$val0 = $val8-($val9*7);

return $val0;
}

vall

13 и 14 это втрой январь и февраль предыдущего года. видимо чтоб всё в целых считать.
какая-то баянистая формула похоже, придуманная когда флоатов ещё небыло или были они дорогими.
Оставить комментарий
Имя или ник:
Комментарий: