Обсудим? PHP: <?php $query = Query::select('[n.*], [a.name] AS [author]') ->from(array('news' => 'n')) ->join(array('authors' => 'a'), '[a.id] = [n.author_id]') ->where('[n.active] = 1') ->offset(($page - 1) * 10) ->limit($items_per_page) ; if ($sort_by_popular) { $query->andWhere('[n.rating] > ?', $min_rating); $query->orderByDesc('[n.rating]'); } else { $query->orderByDesc('[n.id]'); } MySQL: [sql] SELECT `n`.*, `a`.`name` AS `author` FROM `news` `n` INNER JOIN `authors` `a` ON (`a`.`id` = `n`.`author_id`) WHERE `n`.`active` = 1 AND `n`.`rating` > 10 ORDER BY `n`.`rating` DESC LIMIT 2, 10 [/sql] В идеале - полная абстракция над SQL и не зависимость от СУБД Возможность формировать запросы на лету в любом порядке.
Sergey89 Бесперспективно, имхо. Такой подход реализован в LIMB и onPHP. Въехать и нормально изучить эту систему объектов практически невозможно Причем если в LIMB это сделано просто замороченно, то в onPHP тихий ужас. Полная абстракция и независимость таким образом все равно недостижима; попробуй придумать объектную обертку для запроса, например [sql]SELECT p.*, b.* FROM players p, bands b WHERE p.id IN (10,15,17,20) AND p.bandid=b.id ORDER BY p.name[/sql] А потом подумай, сколько людей предпочтут написать такую же конструкцию на PHP, вместо того, чтобы написать нормальный честный SQL-запрос.
[sql]SELECT p.*, b.* FROM players p, bands b WHERE p.id IN (10,15,17,20) AND p.bandid=b.id ORDER BY p.name[/sql] PHP: <?php Query::select('[p.*], [b.*]') ->from(array('players' => 'p')) ->join(array('bands' => 'b'), '[p.bandid] = [b.id]') ->whereIn('[p.id]', array(10, 15, 17, 20)) ->orderBy('[p.name]');
Наверное, даже не независимость а просто возможность сменить диалект на другой. Но часть таких запросов, в принципе, переносима от одной СУБД к другой.
Sergey89 Угу. Теперь смотри, я давно не работал с этим проектом и мне надо что-то подправить в запросе. Увидев эту обертку, я должен сначала мысленно превратить ее обратно в SQL, чтобы понять, что именно она запрашивает из базы, потом мысленно же внести изменения и превратить эти изменения в изменения в последовательности вызовов методов. Если, конечно, каких-нибудь граблей по пути не окажется, все-таки SQL хорошо известен, изучен и отлажен, а твои "доспехи" - пока нет Поэтому их лучше в виде запросов и оставить
Програмно-генерируемые запросы удобны когда не известен список условий, таблицы выборок, полей сортировок и т.д. В нашей реализации ORM используем похожую схему (но проще), так человекам легче юзать его чем писать SQL, мне и самому его на порядок проще воспринимать
Возможно мысленно этого делать не придётся PHP: print $query; и запрос перед тобой. Ну например в пиджи, насколько мне известно, LIMIT 30, 10 запишется как LIMIT 10 OFFSET 30.
Это часть фреймверка, если выкладывать то в виде отдельного проекта или весь фреймверк, или попробовать вынести в Zend_Framework - штука действительно вышла удобная. Мы стали ее разрабатывать ибо существующие реализации ORM являлись неудобным/нерабочими Вся штуковина состоит из следующих частей: - Схема (Scheme) - какие у нас таблицы и связи - Список (List) - выборка - Объект - Драйвер - реализация под конкретную БД Это полнофункциональная ORM, по этому покажу как это в конечном счете выглядит PHP: <? // Установка // Создаем таблицы: $scheme = ORM_Scheme::GetInstance(); $a = $scheme->add('A'); $a->add('name')->setNotNull(); $a->add('type')->setType('int')->setDefault(0); $b = $scheme->add('B'); $b->add('a')->setType('A'); $scheme->save(); // Использование // Создаем объекты $a = new A; $a->name = 'waka'; $a->save(); $b = new B; $b->a = $a; $b->save(); // Получаем // Список - это итератор, по нему можно бегать через foreach $a = ORM_List::Get('A')->getOne(); // первый объект $a = ORM_List::GetById('A', 1); // выборка по id $list = ORM_List::Get('A'); $list->name = 'waka'; $a = $list->getOne(); $list = ORM_List::Get('A'); $list->bond('B')->equals('a', $list->id)->equals('id', 123); $list->limit(10); $list->offset(10); $count = count($list); // без лимита $list->countInResult(); // с учетом лимита // Есть возможность впрыскивать SQL во все его части $list = ORM_List::Get('A'); $list->getQueryBuilder()->addWhere("$list->name IN (SELECT ...)"); foreach($list as $a) ; // Система покрыта тестами (в т.ч. на SQL-инъекции), стабильна.
Ti, а схема и модель умеют разворачиваться из какой-нить внешней конфигурации? Например XML или ini файла.
Нет, мне казалась эта система дикой и неудобной. Схема хранится в серилизованном виде в отдельной таблице. К этой штуке уже прирасло что то типа PhpOrmAdmin =) Классы наследуют ORM_Object. Создавать их необязательно: при их отсутствии автолоадер ORM создаст пустой класс eval("class $class extends ORM_Object {}");
Мне нравится решение с использованием Data Mapper, когда модель ничего не знает ни о базе данных, ни о таблице. PHP: <?php // выборка $user_mapper = new UserMapper(); $user = $user_mapper->findById(123); print $user->getId(); // сохранение $news = new News(); $news->setTitle('Hello, world!'); $news_mapper = new NewsMapper(); $news_mapper->save($news); print $news->getId(); // выведет ID только что вставленной записи
держите: http://stelur.ru/Modules_r5712.tbz 41 Кб. PHP5. Драйвер только для pgsql, юзает PDO PHP: <? set_include_path(get_include_path().PATH_SEPARATOR.'/path/to/Modules'); require_once 'Init.php'; Configuration::GetInstance()->set( 'DataBase', array('dns'=>'pgsql:host=localhost;port=5432;dbname=test', 'user'=>'test', 'password'=>'test') ); ORM::Init(); // поехали!
Ключевое слово Doctrine - на данный момент большинство сходиться во мнении, что это лучшая реализация ORM для PHP З.Ы. Мотороллер не мой - я просто разместил объяву