Допустим у нас есть объект — курсор из субд. Но мы хотим расширить его, для возвращения объектов. Добавить метод next_object(), который будет использовать метод getNext()
Проблема:
Мы не можем заставить модель возвращать объекты другого типа.
Решение 1: сделать итератор на этот курсор(который тоже является итератором). Сделать много магии для методов и свойств и получить новый итератор с нужным нам свойством.
Решение 2: Для PHP 5.4.0, использовать анонимные функции. О нём я и расскажу:
Внутри мапера, в методе findWhere($where) сделаем добавление анонимной функции в курсор, с передачей колбека и самого курсора
/** * @return MongoCursor */ public function findWhere($where) { $collection=$this->collection->find($where); $callback=$this; $collection->next_object=function() use ($collection, $callback) { $data=$collection->getNext(); $obj=$callback->create_object($data); return ($obj); }; return ($collection); }
Теперь, по идее можно обращаться к $collection->next_object() и получать объект, созданный мапером. Но к сожалению это не работает, т.к. PHP попробует выполнить user_func_array и не найдёт метод. Для этого присваиваем свойство переменной, значение которого является анонимной функцией и выполняем, через $func():
public function findOneWhere($where) { $collection=$this->findWhere($where); $func=$collection->next_object; return ($func()); }
Кривенько, но работает.
Агрегатор купонов http://otkuponer.ru:
Факты:
Агрегируется 22 сайта.
Уже в системе более 64 тысяч купонов.
Из 42 купонных сайтов(кому я написал) дали ответ только 22, остальные или проигнорировали мои письма, или затянули с ответом. Под остальных придётся писать парсеры, если так и не ответят.
Партнёрку предложили 10 сайтов.
Вполне устраивающее меня состояние дел =)
В планах:
1. пинговалка поисковых систем — таки хочу немного поиметь поискового трафика
2. поисковый движок.
3. добавить информационные страницы.
Забавная особенность метода count(), применительно к курсору результата запроса.
$items=$collection->find($where)->sort($sort)->limit($limit)->skip($offset*$limit); var_dump($items->count()); //1 var_dump($items->count(true)); //2
в первом случае получим count всех записей, без учёта limit и skip, но с учётом where
во втором случае получим count записей с учётом limit, skip, where.
Готова первая версия агрегатора купонов http://otkuponer.ru/ (если не открывается, ждите пока приедут DNS-ы).
Как обычно минимальный дизайн. На текущий момент работает 2 системы купонов(биглион и групон), на подходе купикупон.
Проект выполняется в чистом стиле проектирования по модели предметной области, но без Active Record (ty to CanceRus). Для разработки используется Agile подход.
СУБД юзается MongoDB, программировать мега-приятно (ty to 10gen).
Фреймворк для разработки — старичок CodeIgniter (но по скорости работы он весьма не старичок).
В планах сделать парсеры на 100500 сайтов купонов и что-нить улучшить для посетителей.
Книга «PHP and MongoDB Web Development Beginner’s Guide (Rubayeet Islam)» достаточна интересна, описывает основные принципы работы с MongoDB из PHP. Читается быстро, за 4-8 часов(если пролистывать примеры =) ). Что особенно порадовало — разбирается map-reduce.
Это единственная книжка, которую нашёл по связке PHP — MongoDB, так что настоятельно рекомендую.
Ещё хотел бы написать о канале #mongodb на IRC freenode. Там более 300 человек, легко идут на контакт, так что велком.
В интернетах бытует мнение, что хранить файлы в БД — школоло. Разрушаем стереотип с Mongo GridFS.
Введение:
MongoDB предоставляет возможность хранить файлы любого размера в Mongo GridFS
Для драйвера PHP — это обычная коллекция, в которой есть поле для хранения бинарных данных.
Бинарные данные в свою очередь разбиваются на чунки(небольшие куски).
Бинарные данные можно извлечь как целиком, так и почунково(это экономит память).
Плюсы хранения данных в БД MongoDB:
MongoDB может хранить миллионы файлов в БД, хранение такого объёма данных в файловой системе может вызвать проблемы(начиная с IO проблем, заканчивая проблемами доступа к данным из разных серверов).
MongoDB реплицирует данные на все сервера, что упрощает доступ к данным на отдельно взятом сервере.
Нет проблем с бекапами и целостностью данных на разных серверах. надёжность обеспечивается MongoDB.
К загруженному файлу можно прикрепить метаданные (описание, комментарии, лайки, и.т.д.)
Т.к. файлы хранятся в чунках — можно работать с любой частью файла.
Минусы:
Быстродействие ниже, чем nginx + файловая истема. Сравним:
nginx + файловая система, получаем 6559.31 операций в секунду
apache + файловая система: получаем 2625.37 операций в секунду
nginx + модуль nginx-gridfs, получаем 1083.88 операций в секунду
Вывод: nginx + fs на высоте, но это решение не даёт масштабируемости и гибкости.
nginx + nginx-gridfs даёт приличные результаты, которые существенно возрастут, при использовании нескольких серверов MongoDB.
Благодарность, http://tokarchuk.ru за тесты.
Задача: найти уникальные значения определённого поля документов коллекции.
Пример:
Создадим документы
db.comments.save({"user": {"points": 25}}) db.comments.save({"user": {"points": 31}}) db.comments.save({"user": {"points": 25}})
Решение:
db.comments.distinct("user.points"); [ 25, 31 ]
Решение на PHP:
$points=$this->mongo_base->command(array("distinct" => "comments", "key" => "user.points"));
Думаю, что это map-reduce, следовательно индексы не юзаются. Если коллекция большая — лучше эти элементы дублировать в другую коллекцию, или, подправить модель предметной области, сократив подобные запросы к минимуму.
Думаю читателям будет интересно, что IAD, кроме того, чтобы постить лыбдыбры, кое-что сделал для улучшения экологии планеты сайтостроителей.
Напоминаю, что есть uneval.com — мой сервис проверки PHP кода на вредоносные конструкции.
А теперь 5 копеек, почему не следует вставлять свеже-скачанный PHP код(тему, плагин, и.т.д.) сразу на сайт:
1. Там может быть бэкдор(PHP код дающий полноценный shell на ваш сервер, хостинг, VDS). Получив такой доступ можно просмотреть, изменить(если права позволяют) любой файл сайта. И файлы других Ваших сайтов. Это позволит использовать Ваши сайты в своих целях, или просто сломать их, или поместить мелкую трудно-отлавливаемую ошибку, изведя жертву до нервного срыва. =)
2. Разместить код в тему сайта, который будет отображать ссылки только поисковым роботам(клоакинг). Средства тёмного SEO.
3. Украсть/изменить информацию сайта.
Ранее писал о безопасности: Бэкдор с триггерах субд, WordPress, eval.
В общем, юзайте мой сервис uneval.com
Многобуков. Всё под катом.
Качаем ericmann-gReader-Library с гитхаба.
Сразу проезжаемся рубанком, заменяя private на protected, иначе нельзя обратиться к методам и свойствам при расширении класса extends.
Задача: вытянуть из Google Reader записи, отмеченные звёздочками (starred) и отослать информацию о снятии звёздочки
Решение, расширяем базовый класс двумя методами:
include_once 'ericmann-gReader-Library/greader.class.php'; class google_reader extends JDMReader { public function __construct() { } public function connect($username, $password) { parent::__construct($username, $password); } // List a particular number of unread posts from the user's reading list public function getStarred($limit) { $gUrl = 'http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/starred'; $args = sprintf('n=%2$s&ck=%3$s&client=GoogleReaderDashboard', time() - (7*24*3600), $limit, time()); $data = $this->_httpGet($gUrl, $args); $decoded_data = json_decode($data, true); $feed_items = $decoded_data['items']; return($feed_items); } /** * Mark this an item as unstarred * * This method marks an item as unstarred for a certain user. * * @param string $itemId The item id that can be retrieved from $this->listAll() * * @return boolean */ public function markAsUnstarred($itemId) { $data = sprintf( 'i=%1$s&T=%2$s&r=%3$s&ac=edit', $itemId, $this->_token,'user/-/state/com.google/starred' ); $path = '/reader/api/0/edit-tag?client=-'; $host = 'www.google.com'; $response = $this->_httpPost($host, $path, $data); if($response == null) return false; return true; } }
юзаем:
$this->load->library('google_reader'); $this->google_reader->connect('login@gmail.com','******'); if ($this->google_reader->loaded) { print_r($this->google_reader->getStarred(10)); }