RSS Записей | RSS Комментариев
Ядоблог - Stay upwind

Архив ‘CodeIgniter’ Категории

sphinx, xmlpipe2, PHP и MongoDB или загибаем трубу из MongoDB в сфинкс, используя PHP

мая 22, 2012 | Комментариев нет

ТЗ: организовать релевантный поиск.

ТУ: данные хранятся в MongoDB, бэкенд на PHP.
Читать полностью »

otkuponer.ru / released

февраля 26, 2012 | Комментариев нет

Готова первая версия агрегатора купонов http://otkuponer.ru/ (если не открывается, ждите пока приедут DNS-ы).

Как обычно минимальный дизайн. На текущий момент работает 2 системы купонов(биглион и групон), на подходе купикупон.

Проект выполняется в чистом стиле проектирования по модели предметной области, но без Active Record (ty to CanceRus). Для разработки используется Agile подход.

СУБД юзается MongoDB, программировать мега-приятно (ty to 10gen).

Фреймворк для разработки — старичок CodeIgniter (но по скорости работы он весьма не старичок).

 

В планах сделать парсеры на 100500 сайтов купонов и что-нить улучшить для посетителей.

CodeIgniter вложенные каталоги для контроллера

января 8, 2012 | Комментариев нет

Фреймворк CodeIgniter из коробки позволяет расположить контроллеры только в одноуровневой папке: dir/file/class/params

Порой случается нужно сделать dir многоуровневой, например: admin/panel/sites/user/add

При попытке обратиться к странице получим ошибку, файл контроллера не найден.

Решение (для CI 2.0, 2.1):

Создать файл /application/core/MY_Router.php

Скопировать в него:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 
/**
* MY_Router Class *
* An extension to the core router class to allow controllers to be in multi-level directories.
*
* @package Router
* @version 1.0
* @author Damien K.
* @copyright Copyright (c) 2010, Ollie Rattue
* @license http://www.opensource.org/licenses/mit-license.php
* @link https://github.com/ollierattue/codeigniter-multi-level-controller-extension
*/
 
class MY_Router extends CI_Router {
 
    // --------------------------------------------------------------------
 
    /**
* OVERRIDE
*
* Validates the supplied segments. Attempts to determine the path to
* the controller.
*
* @access private
* @param array
* @return array
*/
    function _validate_request($segments)
    {
        if (count($segments) == 0)
        {
            return $segments;
        }
 
        // Does the requested controller exist in the root folder?
        if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
        {
            return $segments;
        }
 
        // Is the controller in a sub-folder?
        if (is_dir(APPPATH.'controllers/'.$segments[0]))
        {
            // @edit: Support multi-level sub-folders
            $dir = '';
 
do
            {
                if (strlen($dir) > 0)
                {
                    $dir .= '/';
                }
 
$dir .= $segments[0];
                $segments = array_slice($segments, 1);
            } while (count($segments) > 0 && is_dir(APPPATH.'controllers/'.$dir.'/'.$segments[0]));
 
  // Set the directory and remove it from the segment array
            $this->set_directory($dir);
            // @edit: END
 
            // @edit: If no controller found, use 'default_controller' as defined in 'config/routes.php'
            if (count($segments) > 0 && ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
            {
                array_unshift($segments, $this->default_controller);
            }
            // @edit: END
 
            if (count($segments) > 0)
            {
                // Does the requested controller exist in the sub-folder?
                if (!file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
                {
                    // show_404($this->fetch_directory().$segments[0]);
                    // @edit: Fix a "bug" where show_404 is called before all the core classes are loaded
                    $this->directory = '';
                    // @edit: END
                }
            }
            else
            {
                // Is the method being specified in the route?
                if (strpos($this->default_controller, '/') !== FALSE)
                {
                    $x = explode('/', $this->default_controller);
 
                    $this->set_class($x[0]);
                    $this->set_method($x[1]);
                }
                else
                {
                    $this->set_class($this->default_controller);
                    $this->set_method('index');
                }
 
                // Does the default controller exist in the sub-folder?
                if (!file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
                {
                    $this->directory = '';
                    return array();
                }
 
            }
 
            return $segments;
        }
 
        // If we've gotten this far it means that the URI does not correlate to a valid
        // controller class. We will now see if there is an override
        if (!empty($this->routes['404_override']))
        {
            $x = explode('/', $this->routes['404_override']);
 
            $this->set_class($x[0]);
            $this->set_method(isset($x[1]) ? $x[1] : 'index');
 
            return $x;
        }
 
        // Nothing else to do at this point but show a 404
        show_404($segments[0]);
    }
 
    // --------------------------------------------------------------------
 
    /**
* OVERRIDE
*
* Set the directory name
*
* @access public
* @param string
* @return void
*/
    function set_directory($dir)
    {
        $this->directory = str_replace(array('.'), '', $dir).'/'; // @edit: Preserve '/'
    }
 
    // --------------------------------------------------------------------
}

Благодарность https://github.com/ollierattue/codeigniter-multi-level-controller-extension

CodeIgniter + MongoDB + NetBeans 7.0. Mongo() class code completion.

декабря 12, 2011 | Комментариев нет

Многобуков. Всё под катом.

Читать полностью »

Google Reader, работаем из PHP

декабря 7, 2011 | Комментариев нет

Качаем 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));
		}

ion_auth, CodeIgniter, библиотека авторизации

декабря 7, 2011 | Комментариев нет

Реализация доставляет. Даже observer натянули на библиотеку.

Неоптимальная либа.
На каждое обращение к либе — обращение к базе, часто с join по всем трём таблицам.

Класс для Codeigniter и MongoDB

апреля 29, 2011 | 4 Комментариев

Пользуюсь библиотекой от Алекса Биби для работы с MongoDB в стиле active record. Мне там не хватало пары функций. Добавил их.

За основу взята эта штука http://alexbilbie.blogs.lincoln.ac.uk/2010/04/17/a-codeigniter-active-record-library-for-mongodb/
Но в ней не хватало методов выборки первой записи удовлетворяющей критерию и метода замены объекта в коллекции

Добавлены методы:
get_one — использует метод findone. Формат вызова get_one($collection = «»)
Пример: @usage = $this->mongo_db->where(array(‘bar’ => ‘something’))->get_one(‘foo’);

replace — заменяет элемент на новый. Формат вызова replace($collection = «», $data = array())
Пример: @usage = $this->mongo_db->where(array(‘bar’ => ‘something’))->replace(‘foo’, $data = array());

Скачать: mongo_db_driver.rar

MySQL vs MongoDB. Более сложный insert (для mongodb) часть 3.

марта 25, 2011 | Комментариев нет

В продолжение решения задачи: Реляционка или не реляционка

Пройдя первый этап: MySQL vs MongoDB. Сравнение скорости insert Создания пользователей.

Пройдя второй этап:MySQL vs MongoDB. Более сложный insert (для mongodb) Создания друзей.

Пройдя третий этап: MySQL vs MongoDB. Более сложный insert (для mongodb) часть 2. Создания фотографий у каждого пользователя

Теперь пометим у каждого френда (100 френдов у каждого из 1000 пользователей) по 50 фотографий из 100 просмотренными. Получается 1000*100*50=5000000 (пять миллионов записей):

Для MySQL это вставка в таблицу who_view_photo записи с id_photo и id_user просмотревшем его.

Для MongoDB это изменение существующих объектов в коллекции пользователей. Причём мы меняем 3-й уровень объекта в глубину:

Посмотрим насколько шустро отработает наш MongoDB. Ему уже приходится в каждом объекте содержать информацию о 100 друзьях и 100 фотографиях (1000 записей).

По оси Х-время, по оси Y-количество случаев с данным временем. На примере вставки первых 50000 элементов (просмотров фотографий):

Вывод: Объекты MongoDB разжирнев, обрабатываются дольше, чем банальная вставка значения в таблицу MySQL. Но даже содержа 1000 значений в объекте, выполняя фактически операцию update к объекту (меняя его содержимое) мы получаем все-лишь двукратное замедление, что радует.

MySQL vs MongoDB. Более сложный insert (для mongodb) часть 2.

марта 25, 2011 | Комментариев нет

В продолжение решения задачи: Реляционка или не реляционка

Пройдя первый этап: MySQL vs MongoDB. Сравнение скорости insert Создания пользователей.

Пройдя второй этап:MySQL vs MongoDB. Более сложный insert (для mongodb) Создания друзей.

Создадим фотографии пользователей. Каждому пользователю по 100 фотографий.

Для MySQL это обычное создание записи в таблице:

Для NoSQL это добавление в коллекцию пользователей из MySQL vs MongoDB. Более сложный insert (для mongodb) ещё одного поля photos, являющегося множеством элементов.

И сравниваем производительность выполнения вставки 100 фотографий каждому из 1000 пользователей (100000 элементов).

По оси Х-время в секундах, по оси Y-количество случаев с данным временем выполнения(основной кусок увеличен):

Вывод: нужно понимать, что для MySQL — создание фотографии лишь вставка новой записи в таблицу фотографий, а для MongoDB — изменение существующего объекта, в котором уже содержится 100 друзей, созданных в предыдущем этапе, и ещё добавляется 100 элементов фотографий. Но при всём этом MongoDB половину операций (~50000) отработало за 0.0001 секунды, а MySQL редко ранее 0.0005 секунды. И MongoDB пришлось проделать гораздо большую работу (обновить существующие объекты).

MySQL vs MongoDB. Более сложный insert (для mongodb)

марта 25, 2011 | Комментариев нет

В продолжение решения задачи: Реляционка или не реляционка

Пройдя первый этап: MySQL vs MongoDB. Сравнение скорости insert

Создадим для 1000 пользователей системы по 100 друзей каждому (1000*100=100000 записей).
В MySQL мы имеем структуру:

Таким образом для MySQL это будет вставка записи в таблицу friends.
В MongoDB мы имеем структуру сложнее. т.к. вся информация представлена в виде большого дерева.
Мы имеем коллекцию пользователей. В которой все наши 1000 пользователей как документы.
Каждый документ имеет структуру:

  1. поле id_user
  2. массив ifriends состоящий из элементов id_friend (ссылка на другой элемент этой же коллекции users)

Понять сложно, если вы до этого ни разу не делали nosql проект. Обязательно изучите.
Ну так вот, операция создания френдов, по времени (по оси Х-время в секундах, по оси Y-количество операций с этим временем выполнения):

Вывод: тут нужно понимать, что в mysql — обычная вставка данных в таблицу, а в MongoDB — обновление существующего объекта — создание в его поле ifriend ещё одного элемента, хоть и с одним значением id_friend, но всё-же это update. Ну и мы видим, что выполнив 100000 таких операций мы имеет сравнимо одинаковые затраты по времени.

© 2010 Ядоблог. Все права защишены.
Powered by Лаборатория Яда. Написать администратору