Список всех-всех контроллеров

Понадобилось мне получить список всех контроллеров приложения, включая контроллеры плагинов. Configure::listObjects, увы, такого не умеет. Этот метод выдает только все контроллеры приложения, но без контроллеров плагинов. Пришлось немного адаптировать метод, предложенный Rob Weaver в гуглогруппе CakePHP. На выходе у моего метода получается объединенный список контроллеров. Основные — как отдает метод listObjects, а принадлежащие плагинам в виде «Plugin.Controller». Можно их поочередно скармливать сразу в App::import. Разве что выбрость те, названия которых на «App» заканчивается, если они не нужны. Будете загружать, помните, что класс «Controller» уже должен быть загружен.

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

30.11.2009 • Рубрики: CakePHP • Comments

Попробуем почитать между строк

Очередное интервью с представителями Яндекса, на этот раз на Roem.Ru. Никаких особенных откровений, конечно, нет. Что же вы хотели, чтоб яндексоиды всем бесплатно раздали рекомендации, как выбраться на первое место в выдаче по любому запросу? Но все-таки что-то надо говорить и из обтекаемых формулировок можно выстраивать разные забавные предположения. Лично я не завсегдатай SEO-форумов, возможно, все это уже где-то обсуждалось — если баян, извините. Читать полностью »

25.11.2009 • Метки:  • Рубрики: Поисковики • Comments

Контроллеры: загрузка

Удивительно но факт. Если из консольного, например, приложения нужно попользоваться моделью, то, само собой, надо загрузить класс модели.

App::import('Model', 'MyModel');

Но загрузить контроллер так не получится. Получим сообщение об ошибке из-за невозможности найти базовый класс Controller. Вот найти класс Model кейк может, а Controller – нет. Во всяком случае 1.2.5. В версии 1.3 не пробовал.

Поэтому приходится загружать его явно и напрямую:

App::import('Core', 'Controller');
App::import('Controller', 'MyController');
23.11.2009 • Метки: , • Рубрики: CakePHP • Comments

Компоненты: перезагрузка

Не знаю, такую-ли ситуацию имел в виду BorisPlus в своем комментарии. Ну, чем богаты. Вообще не хотел это все описывать, потому как код довольно халтурно написан.

Этот код приложения, изначально крутившийся по Cake 1.1, был переписан для какой-то беты Cake 1.2. С текущей версией, 1.2.5, он работает без проблем, но, возможно, нуждается в чистке.

Эта часть программы выполняющий импорт данных от поставщиков. Поставщики предоставляют данные в CSV формате, но порядок колонок, некоторые значения и т.д., конечно разные. Для осмысленной обработки эти полученные данные надо привести к единообразному виду.

Данные поступают из написанного куцего Datasource, скармливанются компоненту, который и выполняет разбор и приведение к общему виду, потом записываются в нашу таблицу.

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

14.10.2009 • Метки: , • Рубрики: CakePHP • Comments

Кэш и консоль

За полноценный пост не считается. Так, узелок на память.

В качестве кэша байткода и переменных я использую XCache. Но при запуске консольных приложений, он у меня не работает. Должен или нет, не знаю, не разбирался. Наверное, не должен, если подумать. :-) Просто отметил, что консольные приложения Cake, включая ‘cake bake‘ высыпают кучу ошибок, если XCache используется, как кэш по умолчанию. Поэтому в конфигурации кэша приложения (APP/config/core.php) на CakePHP добавляю маленькую проверку на тип API.

Вот как-то так:

if (PHP_SAPI == 'cli') {
  Cache::config('default', array('engine' => 'File'));
} else {
  Cache::config('default',
    array(
      'engine' => 'Xcache',
          'prefix' => 'mypfx_',
          'user'=>'IamAdmin',
          'password'=>'c00leztPass'));
}

Можно пользоваться константой PHP_SAPI или функцией php_sapi_name(), не важно.

21.09.2009 • Метки: , , • Рубрики: CakePHP • Comments

CakePHP 1.2, Content-type, debug, RequestHandler и все-все-все

Как правильно, с точки зрения CakePHP, отвечать на запросы, если ответ требуется не html, а, например, json? Я считаю, что для получения ответа в нужном формате надо воспользоваться компонентом RequestHandler и указать в запросе расширение, в нашем случае ‘.json’. URL для запроса получится какой-то такой:

http://www.oursite.com/controller/action.json

Чтобы Cake не пугался этого расширения и вызывал правильное действие (action), в файл APP/config/routes.php добавим строку:

Router::parseExtensions('json');

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

12.09.2009 • Метки: , • Рубрики: CakePHP • Comments

Метрическая аналитика

У Яндекса есть полезная система статистики, похожая частью на Google Analytics, частью — на счетчик Liveinternet. Называется Яндекс.Метрика.

Яндекс.Метрика

Яндекс.Метрика

Все как обычно — устанавливаете код на страницы своего сайта и наблюдаете статистику по посетителям. В отличие от GA, которая обновляется раз в сутки, Метрика показывает результаты практически в реальном времени, как Liveinternet — это гораздо удобнее.

Если сайт рекламируется с помощью Яндекс.Директ и на рекламируемом сайте установлен код для сбора статистики для Директа — это и есть код Метрики!  В этом случае можно уже не напрягаться, а смотреть отчеты.

UPDATE нельзя INSERT

Задача очень простая: обновить запись в таблице, а если подходящей записи нет, добавить новую. Кажется вполне тривиальной задачей. Особенно, если условия, по которым выбирается запись для обновления сделать первичным или уникальным ключом.

Эта тема пару месяцев назад обсуждалась в ЖЖ сообществе ru_php, но как-то вяло. Попробую этим небольшим постом обобщить разные методики.

Самый простой метод: сначала SELECT COUNT(*), а потом, по результатам, либо INSERT, либо UPDATE. Если речь идет об обновлении одной записи на этом методе можно остановиться.

Увы, если надо вставить много записей, этот метод начинает изрядно тормозить. Для такого случая у MySQL есть несколько разных методов.

REPLACE — наиболее очевидный и самый неудобный. Сервер пытается вставить новую строку, если возникает ошибка с повторяющимся уникальным ключом, запись удаляется и потом снова добавляется новая. Увы, при этом значения тех полей, которые обновлять не надо сбрасываются в дефолтные. Использовать эту команду можно, если обновляется полностью вся запись, что случается редко. Например, если в таблице есть колонка `created`, содержащая дату создания записи, то REPLACE уже не подходит. Кроме того, в случае, если большинство записей заменяется, а не добавляется, получается конструкция INSERT-(ошибка)-DELETE-INSERT — три запроса!

INSERT … ON DUPLICATE KEY UPDATE … — этот вариант сильно интереснее. Сервер пытается вставить запись, если получает ошибку c дублирующимся ключом, обновляет поля, указанные в после UPDATE. Работает отлично, в документации сказано, что эта конструкция специально задумана для вставки множества записей. Но пара неприятных моментов есть.

  1. Первый, как мне казалось, довольно экзотичный, но я с ним все-таки столкнулся. В конструкции INSERT можно в качестве источника данных использовать подзапрос SELECT. Но, в случае с INSERT … ON DUPLICATE KEY UPDATE, в подзапросе не должно быть группировки GROUP BY.
  2. Второй момент для многих неважен. Если записей для обновления сильно больше чем новых, то получается все равно много лишних обращений к БД — ведь вся конструкция это сочетание INSERT-(ошибка)-UPDATE. Лучше, чем с REPLACE, но все же…

Долгое время я пользовался UPDATE, а потом проверял mysql_affected_rows() (в CakePHP это $DataSource->lastAffected(). То есть проверял количество обновленных записей и, если получал 0, добавлял запись. У этого метода есть очень существенный недостаток: если среди данных для обновления встречаются две одинаковых строки, то при повторном UPDATE сервер, как самый умный, не обновляет запись. Соответственно число обновленных записей, affected rows, при построчном переборе, будет равно 0 несмотря на то, что такая строка в таблице есть. Соответственно решение о вставки новой строки будет ошибочным.

Устав мириться с ожидаемым, но неприятным поведением mysql_affected_rows() я порылся в документации на MySQL и нашел еще одну функцию: mysql_info(). Она выдает обычную строчку со статистикой последнего запроса к БД, причем для разных типов запросов строчки разные. Для UPDATE выглядит так:

Rows matched: 40 Changed: 40 Warnings: 0

Зато содержит чрезвычайно ценную информацию: количество записей, которые попадают под условие для обновления. Это те цифры, что после слова ‘matched:‘. Ну, регулярное выражение для получения циферок из строки сами напишите? ;-)

P.S. У меня ощущение deja vu — мне кажется, что я уже писал похожий пост. Перерыл дневники — нет такого. Или это черновик был…

12.04.2009 • Метки: , , • Рубрики: MySQL • Comments

Поиск по дате

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

Я не люблю обновлять MySQL ни на своей машине, ни на сервере. Каждый раз гадаешь: «понадобится бэкап или или нет?» Поэтому имеет место некоторая несогласованность версий, на которую я не слишком сильно обращал внимание. До сегодняшнего вечера. Итак у меня в наличии на домашней машине MySQL 5.1.22-rc-community, win32. На сервере соответственно MySQL 5.0.45 из бокса CentOS 5.

Самая обычная таблица в БД, с самым обычным полем created типа DATETIME.

Однако запрос

SELECT
    COUNT(*) AS `count`
FROM
    `offers` AS `Offer`
WHERE
    `Offer`.`supplier_id` = 3 AND
    DATE(`Offer`.`created`) = '2009-03-12'

работает на них совершенно по-разному!

На домашней машине все, как ожидается, возвращается количество записей за 12-е число. На сервере — 0 записей. Читать полностью »

13.03.2009 • Метки: , , • Рубрики: MySQL • Comments

Производительность pagination в CakePHP 1.2

Хотя использование разбивки на страницы (pagination) вполне заслуживает отдельного, обстоятельного поста, хочу остановиться лишь на паре не слишком очевидных моментов. Я предполагаю, что у читающих этот пост есть определенный навык использования CakePHP, моделей вообще и pagination в частности. Этот пост и так получается длинным.

Известно, что метод контроллера paginate() вызывает последовательно два метода модели — первый для подсчета общего числа записей, удовлетворяющих заданным условиям, и второй на выборку указанного числа записей начиная с заданной. Т.е. первый это фактически функция SQL COUNT(*), второй — SELECT … LIMIT n,m.

Именно из-за того, что вызовов методов больше одного, не срабаывает временная привязка/отвязка подчиненных моделей с помощью bindModel()/unbindModel(). Этим методам, для корректной работы с paginate() приходится передавать второй параметр, равный false. К счастью, еть ContainableBehavior, решающий эту проблему.

Эти запросы не всегда оптимальны и есть возможность улучшить производительность метода paginate() именно за счет оптимизации собственно самих запросов. Читать полностью »

Related Posts with Thumbnails