Подскажите, как правильно спроектировать фотогалерею, исходя из имеющейся БД? Исходные вводные - есть 2 таблички, 1-я называется cats, разделы, поля id int4 и name varchar(250). Вторая - images, поля - id int4, filename varchar(250), category int4 (здесь ссылка на cats - FK cats(id) ). Впрямую пишу вот так: Код (Text): class category { private $id; private $name; ... геттеры/сеттеры } class image { private $id; private $filename; private $category; ... геттеры/сеттеры } И сразу затык. 1) как лучше организовать загрузку/инициализацию объекта из БД? Как мне увязать (грамотно) например PDO и первый класс category? Какой-то шаблон надо применить, или как? 2) Как повязать category и image? Варианты навскидку - а) хранить в $category соответствующий объект ; б) переименовать поля в классе category и унаследовать от него image ; в) какой-то типовой прием? Пытаюсь разобраться с ООД, но от книжек ООП уже реально, голова кругом. Хочется чтоб как в best practices, а умеется пока только процедурный код
геттеры/сеттеры это из мира Java. связь с PDO может быть очень простой - там где надо, просто обращайся к PDO ))) не пытайся родить еще один мега-универсальный ORM, говно получится класс Категории точно нужен? я бы ограничился просто атрибутом категория в классе Изображение. тебе понадобится какая-то Коллекция изображений, выбор по категории будет одним из вариантов фильтра. хочешь красивости и ООПности - сделай Коллекцию как воплощение интерфейса Iterator.
Спасибо за советы, насчет итератора - так и сделаю. Про откусить категорию - там в реале еще есть поле parent, которое собственно дает доп.табличке смысл.
Раньше, когда программисты растили бороды, считалось что наследование это круто. Сейчас все сходятся на том, что интерфейсы это хорошо, а наследование привносит ненужную сложность. Попробуй сделать так: опиши что должно происходить, только методы, переменные отложи на потом. Методы сгруппируй в интерфейсы, например: МенеджерФайлов умеет аплодить, отдавать и удалять файлы. МенеджерКартинок умеет создавать копии других размеров, накладывать вотермарк. КоллекцияКартинок создает коллекции по заданным условиям и т.д Накопится пяток таких групп по функциям. Потом возможно ты воплотишь МенеджерФайлов и МенеджерКартинок в одном классе Изображение, а возможно в разных. В любом случае тебе удобно будет описывать передаваемый куда-то объект как соответствующий тому или иному интерфейсу. Скорее всего это закончится очередным недоделанным хламом, зато ты прокачаешь своё абстрактное мышление
Пример чисто умозрительный пока. Разбираюсь в ООП. Процедурный код на пыхе пишу с 2001, есть в т.ч. свой движок для магазинов (проектировался в свое время с оглядкой на osCommerce 2.1), но что-то ветреная мода стала все больше ООП (считал такой код сильно избыточным для разработчика-одиночки, но пора бы уже перестать от него шарахаться Сейчас на ряде сайтов пришел к простейшей CMS, которая меня не устраивает именно тем, что ООП (как мне кажется) может полечить - наращиваемостью ядра и легкостью ведения разных веток, допиленных для разных клиентов. Изучаю Зандстру, но что-то мозг подзаплыл Добавлено спустя 3 минуты 6 секунд: Код для категорий CMS, вместе с хранилищем. Пока получается вот такое чудище: Код (Text): class Category { private $id = NULL; private $name = NULL; private $parent = "NULL"; private $updated = 0; function __construct($myid = NULL, $myname = NULL, $myparent = NULL) { if (isset($myid)) { $this->id = $myid; } if (isset($myname)) { $this->name = $myname; } if (isset($myparent)) { $this->parent = $myparent; } } function getId() { return $this->id; } function getName() { return $this->name; } function setName($in) { if (!is_null($this->name) || $this->name != $in) { $this->updated = 1; } $this->name = $in; } function getParent() { return $this->parent; } function setParent($in) { if (!is_null($this->parent) || $this->parent != $in) { $this->updated = 1; } $this->parent = $in; } function isUpdated() { return ($this->updated); } function __destruct() { echo "Destructor for '{$this->id}' '{$this->name}' fired<br />\n"; } function __toString() { return "Object Category({$this->id}, '{$this->name}', {$this->parent}, {$this->updated})"; } } class CategoryCollection implements Iterator { private $var = array(); public function __construct($array) { if (is_array($array)) { $this->var = $array; } } public function rewind() { reset($this->var); } public function current() { $var = current($this->var); return $var; } public function key() { $var = key($this->var); return $var; } public function next() { $var = next($this->var); return $var; } public function valid() { $key = key($this->var); $var = ($key !== NULL && $key !== FALSE); return $var; } } Все еще думаю, как замапить его на базу.
Реконструировал все. index.php Код (Text): spl_autoload_register(function ($class){ if(!file_exists($class.'.php')) { $class = strtolower($class); } include $class.'.php'; }); abstract class CMSCollection implements Iterator { use varIterator; protected $var = array(); public function __construct($array) { if (is_array($array)) { $this->var = $array; } } } class ItemCollection extends CMSCollection { function add(Item $i) { $this->var[] = $i; } } class ItemDbLink { static function getById($id, PDO $db) { $res = $db->query("SELECT * FROM items WHERE id=$id"); if($data = $res->fetch(PDO::FETCH_ASSOC)) { return new Item($data); } else { throw new Exception('Cannot find item with id='.$id); } } } $conf = new Config(); echo $conf['db']['dsn'],'<br />'; $db = new PDO($conf['db']['dsn'], $conf['db']['login'], $conf['db']['password']); try { $getdata = ItemDbLink::getById(1, $db); foreach ($getdata as $key=>$val) { echo $key, "-", ($key!='type')?$val:ItemTypes::getDescription($val), "<br />\n"; } } catch (Exception $e) { echo "Fired: ", $e->getMessage(); } config.php (по мотивам TodoList примера из Netbeans) Код (Text): class Config implements ArrayAccess { private $data = null; public function __construct() { $this->data = parse_ini_file('config.ini', true); } public function offsetSet($offset, $value) { throw new Exception('config is read only'); } public function offsetExists($offset) { return isset($this->data[$offset]); } public function offsetUnset($offset) { throw new Exception('config is read only'); } public function offsetGet($offset) { return isset($this->data[$offset]) ? $this->data[$offset] : null; } } item.php: Код (Text): class Item implements Iterator { use varIterator; private $var; private $updated = 0; public function __construct(array $var = NULL) { if(isset($var) && is_array($var)) { $this->var = $var; } else { $this->var = array(); } if(!key_exists('parent', $this->var)) { $this->var['parent'] = 'NULL'; } } public function __get($k) { return $this->var[$k]; } public function __set($k, $v) { if($k != 'id') { $this->var[$k]=$v; } else { throw new Exception('Cannot set `id` field'); } $this->updated = 1; } public function __isset($k) { return key_exists($k, $this->var); } public function isUpdated() { return $this->updated; } } itemtypes.php: Код (Text): abstract class ItemTypes { const TEXT = 1; const NEWS = 2; const SYSTEM = 4; /** * Returns type description * @param int $type item type (number) * @return string description */ static function getDescription($type) { $res = ''; switch ($type) { case ItemTypes::TEXT: $res = 'Тексты'; break; case ItemTypes::NEWS: $res = 'Новости'; break; case ItemTypes::SYSTEM: $res = 'Служебные страницы'; break; default: $res = 'не определено'; break; } return $res; } } и наконец трейты - varIterator.php Код (Text): trait varIterator { public function rewind() { reset($this->var); } public function current() { $var = current($this->var); return $var; } public function key() { $var = key($this->var); return $var; } public function next() { $var = next($this->var); return $var; } public function valid() { $key = key($this->var); $var = ($key !== NULL && $key !== FALSE); return $var; } } С хренову душу функциональности, а кода уже пачка. Одна надежда, что управляемость улучшится. И да, конфигурация ini-шником и сведение всей работы с БД в пару классов - фенечка очень соблазнительная. По магазину например для оптимизации я держу список всех SQL запросов с указанием, какой в каком файле и какой строке содержится - очень выморочно было оптимизировать скорость работы.