Yii: CStatRelation и defaultScope у реляций
Ночью столкнулся с внезапной проблемой в Yii Framework 1.
Оказывается, статистические запросы через CStatRelation не поддерживает scopes в параметрах. Так что, если мы имеем defaultScope в нашем связанно элементе, то отменить его действие мы не сможем.
Как повторить эту проблему. Допустим имеем класс RevisionsSets с таким вот определением реляций:
/** * @return array relational rules. */ public function relations() { return array( 'pricesCount'=>array( self::STAT, 'Revisions', 'revisionSetId', ), ); }
Ну и в самом классе Revisions имеем следующую группу условий по-умолчанию:
public function defaultScope() { $rev = Config::model()->getConfigValue("Shop", "CurrentRevision")->value; $t = $this->getTableAlias(false, false); return array( 'condition' => "$t.revisionSetId=:rev", 'params' => array('rev'=>$rev), ); }
Что же происходит при вызове $revisionsSetModel->pricesCount?
STAT-реляция забирает количество записей, но при этом применяет defaultScope от модели Revisions и в итоге для всех экземпляров RevisionsSets считается некорректное количество записей.
Исправить такое поведение можно было бы допливанием CStatRelation и реализацией там поддержки условий, как это например сделано в HAS_MANY, MANY_MANY, BELONGS_TO.
Но мне в голову пришел более быстрый вариант. Я создал класс RevisionsResetScope, который наследует Revisions и переопределяет defaultScope:
<?php class RevisionsResetScope extends Revisions { public function defaultScope() { return $this->resetScope(); } }
Этот класс и используем в реляции:
/** * @return array relational rules. */ public function relations() { return array( 'pricesCount'=>array( self::STAT, 'RevisionsResetScope', 'revisionSetId', ), ); }
Вот такой костыль. Кстати, на форуме Qiang Xue решил отказаться от статистических запросов и CStatRelation в Yii Framework 2. Будем надеяться, что разработчики предусмотрят достойную замену.
Бесконечная очередь и отказ от сообщений в RabbitMQ + Thumper + PHP AMPQlib
По работе столкнулся с одной задачей и решил использовать сервер очереди сообщений RabbitMQ в связке с PHP 5.3 через PHP-ampqlib и библиотеку Thumper.
Чтобы сделать Consumer(worker), который будет обрабатывать бесконечную очередь надо задать $consumer->consume(-1);. Тогда в цикле Thumper, где проверяется нужно ли нам выходить всё будет хорошо и наш обработчик будет работать вечно.
Но появился ещё один интересный вопрос - что делать, если внутри функции обработки сообщения произошел Exception? Я решил, что тогда мы отказываемся от этого сообщение и оно идет к другому обработчику. Реализовывать это лучше расширением класса Consumer:
class MyConsumer extends Consumer {
public function processMessage($msg) {
try {
parent::processMessage($msg);
} catch (Exception $e) {
echo "Message rejected due to exception.\n".$e->getMessage()."\n";
$msg->delivery_info['channel']->basic_reject($msg->delivery_info['delivery_tag'],true);
//throw $e;
}
}
}Соответственно ваш Consumer должен быть объектом класса MyConsumer.
Если же всё таки нужно кинуть этот Exception выше - убираем комментарий со строки throw $e;
Блог основателя SEO в Demis Group
Директор тамбовского подразделения Demis Group, один из основателей SEO в нашей компании наконец то начал приобщаться к общению в сети. Раньше просто совсем времени не было.
Вот встречайте:
Twitter: @skobeeff
Запаковываем проект в один JAR для запуска на Hadoop
Сегодня потребовалось запаковать проект на Java в один JAR файл. Нужно мне это для того, чтобы не мучиться с подключением сторонних jar и их дистрибьюции на кластер Hadoop Map Reduce. Да и к тому же, параметр -libjars из документации у меня почему то не работал.
Как получить MAC из busyBox
Сегодня потребовалось из busybox при установки Debian netsinst получить MAC-адрес сетевки. Это как оказалось можно сделать так:
# ip l sh eth0
17: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:c0:9f:55:6e:e5 brd ff:ff:ff:ff:ff:ff
Лечим Network is unreachable в Java на Debian/Ubuntu
Если у вас в Debian или Ubuntu не работают приложения, написанные на Java, ругаясь при этом как то так:
38 connect(22, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:72.5.124.95", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28
6938 connect(22, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:72.5.124.95", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = -1 ENETUNREACH (Network is unreachable)
Или вот так: Could not bind/listen. Network is unreachable.
Лечится это просто.
Редактируем файл /etc/sysctl.d/bindipv6only.conf и ставим там 0 вместо 1. Перегружаемся и всё работает! Проверено, у меня так hadoop, hbase не хотели запускаться.
Если хотите без перезагрузки, то можете ещё выполнить:
echo 0 > /proc/sys/net/ipv6/bindv6only
sysctl net.ipv6.bindv6only=0
Список регионов яндекса
Не для кого уже не секрет, что Яндекс умеет определять регион сайта. Делает он это автоматически по своим хитрым алгоритмам. С недавнего времени, каждый владелей сайта может сам задать регион сайта в Яндексе. Делается это через панель веб-мастера. А для сайтов в ЯК региональную пренадлежность определяют моедараторы Yandex Каталога.
Ресайз PNG картинок c прозрачностью в Thunar
Сегодня мне пришлось решить рутиную задачу - изменить размер PNG картинок не теряя при этом прозрачность. Искать какой либо софт было лень, поэтому решил написать bash скрипт, который всё сделает за меня.
Перенос MySQL в другую папку/раздел на Debian/Ubuntu
Недавно мне пришлось переместить данные MySQL в другую папку(а точнее на другой раздел) на Debian Linux 5.0.3.
Логично, что простым переносом файлов тут не отделаешься, надо что-то где-то прописать.
По-умолчанию данные MySQL лежат в /var/lib/mysql. Мне захотелось, чтобы они были в /home/mysql. Переносим папку mysql куда нам нужно, затем лезем в конфиг MySQL (/etc/mysql/my.cnf).
Там есть опция:
datadir = /var/lib/mysqlВот её мы заменяем на следующее:
datadir = /home/mysqlПробуем перезапустить наш MySQL сервер (/etc/init.d/mysql restart) и в итоге у нас ничего не запускается.
Смотрим tail /var/log/messages и видим приблизительно такое:
kernel: [ 2714.258037] audit(1227462526.718:13): type=1503 operation="inode_create" requested_mask="w::" denied_mask="w::" name="/home/mysql/yii.test" pid=19659 profile="/usr/sbin/mysqld" namespace="default" kernel: [ 2714.310572] audit(1227462526.770:14): type=1503 operation="inode_permission" requested_mask="rw::" denied_mask="rw::" name="/home/mysql/ibdata1" pid=19659 profile="/usr/sbin/mysqld" namespace="default"
Это нам говорит о том, что политика AppArmor запрещает всякие манипуляции мусклу в новой папке. И это естественно, ведь AppArmor думает, что базы лежат всё там же, в /var/lib/mysql.
Значит надо переписать конфиг AppArmor. Открываем и редактируем /etc/apparmor.d/usr.sbin.mysqld . В нём меняем всё, где упоминается /var/lib/mysql на /home/mysql. Затем перезапускаем AppArmor и MySQL:
sudo service apparmor restart sudo /etc/init.d/mysql restart
И у нас всё работает! Такая же схема для Ubuntu.