суббота, марта 28, 2009

Segmentation fault в php5

Взгляните на этот код:

class A extends B
{
public function getA(){
if($this->getA()){
return $this->getA();
} else {
return $this->getB();
}
}
}

С первого взгляда все верно. Но выдает:
[Sat Mar 28 17:12:59 2009] [notice] child pid 11239 exit signal Segmentation fault (11)

Образуется цикл, тогда надо:

class A extends B
{
public function getA(){
if(parent::getA()){
return parent::getA();
} else {
return $this->getB();
}
}
}

среда, марта 25, 2009

Монтирование(mount) файловой системы по ssh

sudo apt-get install sshfs

sudo sshfs -o allow_other username@hostname.ru:/home/usernamedir /home/usermountdir

Можно создать файл mount.sh и сделать ярлык для запуска на него. Gnome легко определяет что надо ввести пароль для ssh и предлагает сделать это через GUI


вторник, марта 24, 2009

Необходимые компоненты для работы symfony в Ubuntu

Недавно переставил систему с ubuntu 8.10 x86 на ubuntu 9.04 x86_64 на свой рабочий acer aspire 5720G.
Пришлось настраивать систему заново, но так как я сейчас разрабатываю только под symfony. Настраивал работу системы как раз для симфони, поэтому можно рассматривать этот пост как Необходимые компоненты для работы symfony в Ubuntu.
Поставим apache и php5.

sudo apt-get install apache2 php5-mysql libapache2-mod-php5 php5-common mysql-server mysql-common mysql-client-5.0 php5-cli php5-xsl


Устанавливаем кодировку по умолчанию для mysql:

sudo mcedit /etc/mysql/my.cnf
или
sudo nano /etc/mysql/my.cnf

добавить в конец строчки:

default-character-set=utf8
character_set_client=utf8

вместо utf8 можно любую другую, например cp1251

Ставим svn. Если не используете, то можно пропустить.

apt-get install subversion

libapache2-svn ssl-cert libapache2-svn - опционально



Ставим symfony:
sudo mkdir -p /usr/share/php5/symfony-1.2/
sudo svn co http://svn.symfony-project.com/branches/1.2 /usr/share/php5/symfony-1.2/



<VirtualHost 127.0.0.1:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/symfony/web/
<Directory />
Options FollowSymLinks Indexes
AllowOverride All
</Directory>
ErrorLog /var/log/apache2/error.log
LogLevel warn
CustomLog /var/log/apache2/access.log combined
</VirtualHost>


Если проект начинаете с нуля:

wget http://www.symfony-project.org/get/sf_sandbox_1_2.tgz

tar xfz sf_sandbox_1_2.tgz
cp sf_sandbox
mv sf_sandbox/* /var/www/symfony/


Подключаем библиотеку symfony. В файле:
/var/www/symfony/config/ProjectConfiguration.class.php

меняем на:

require_once '/usr/share/php5/symfony-1.2/lib/autoload/sfCoreAutoload.class.php';


Проверяем
cp /var/www/symfony/
./symfony

если все без ошибок, то можно приступать к работе

понедельник, марта 23, 2009

Группировка виджетов(sfWidget) в админ генераторе(admin-generator) для symfony 1.2

Сегодня мне понадобилось динамически стороить элементы формы в админ генераторе и группировать их.
Это оказалось не столь простой задачей.
Я использую symfony 1.2.5-DEV из svn.
Сразу оговорюсь, что строить из embedded forms я не хочу, т.к. элементы динамические.
Вот мои действия. Постоим простейший вариант:

$this->widgetSchema['group0'] = new sfWidgetFormSchema(array(
'name0' => new sfWidgetFormInput(),
'email0' => new sfWidgetFormInput(),
));

сгенерированный html код будет:

<div>
<label for="t_object_key_group0">Group0</label>
<label for="t_object_key_group0_name0">Name0</label>
<input name="t_object_key[group0][name0]" id="t_object_key_group0_name0" type="text">
<label for="t_object_key_group0_email0">Email0</label>
<input name="t_object_key[group0][email0]"id="t_object_key_group0_email0" type="text">
</div>




Смотрится - не очень. Тогда покопавшись в коде я попробовал указать FormFormatter:

$decorator = new sfWidgetFormSchemaFormatterTable($this->widgetSchema
['group0']);
$this->widgetSchema['group0']->addFormFormatter('custom', $decorator);
$this->widgetSchema['group0']->setFormFormatterName('custom');


Эффект тот же.
Тогда я использовал RowFormat:

$this->widgetSchema['group0']->getFormFormatter()->setRowFormat
("\n<table> <th>%label%</th>\n <td>%error%%field%%help%
%hidden_fields
%</td>\n</tr></table>");


сгенерированный html код будет:

<div>
<label for="t_object_key_group0">Group0</label>
<table> <tbody><tr><th><label for="t_object_key_group0_name0"
>Name0</label></th>
<td><input name="t_object_key[group0][name0]"id="t_object_key_group0_name0" type="text"></td>
</tr></tbody></table>
<table> <tbody><tr><th><label
for="t_object_key_group0_email0">Email0</label></th>
<td><input name="t_object_key[group0][email0]" id="t_object_key_group0_email0" type="text"></td>
</tr></tbody></table>
</div>




Тоже не то что я ожидал.
И выходом из всего этого будет:

$this->widgetSchema['group0'] = new sfWidgetFormSchema(array(
'grouped' => new sfWidgetFormSchema(array(
'name0' => new sfWidgetFormInput(),
'email0' => new sfWidgetFormInput(),
))
));
$this->widgetSchema['group0']->getFormFormatter()->setRowFormat
("\n<table> <th>%label%</th>\n <td>%error%%field%%help%
%hidden_fields
%</td>\n</tr></table>");



сгенерированный html код будет:

<div>
<label for="t_object_key_group0">Group0</label>
<table> <tbody><tr><th>Grouped</th>
<td></td></tr><tr>
<th><label for="t_object_key_group0_grouped_name0">Name0</label></th>
<td><input name="t_object_key[group0][grouped][name0]"
id="t_object_key_group0_grouped_name0" type="text"></td>
</tr>
<tr>
<th><label for="t_object_key_group0_grouped_email0">Email0</label></
th>
<td><input name="t_object_key[group0][grouped][email0]"
id="t_object_key_group0_grouped_email0" type="text"></td>
</tr>
</tbody></table>
</div>




Это и есть то что нам надо.
Похоже на то, что setFormFormatterName работает только с embedded forms.
Я пытался попросить объяснений это на гугл групс, но без результатно.

Теперь самое интересное. Как же будет выглядеть валидатор:

$this->validatorSchema['group0'] = new sfValidatorSchema(array(
'grouped' => new sfValidatorSchema(array(
'name0' => new sfValidatorInteger(),
'email0' => new sfValidatorString(),
))
));


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

Интересный момент в mysql 5 для foreign key

CREATE TABLE `key` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`slug` varchar(128) NOT NULL,
`group_id` int(11) NOT NULL,
`parent_id` int(11) default '0',
`comp_id` int(11) default NULL,
PRIMARY KEY (`id`),
KEY `key_FI_1` (`group_id`),
KEY `key_FI_2` (`parent_id`),
CONSTRAINT `key_FK_1` FOREIGN KEY (`group_id`) REFERENCES `key_group` (`id`) ON DELETE CASCADE,
CONSTRAINT `key_FK_2` FOREIGN KEY (`parent_id`) REFERENCES `key` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8


С первого взгляда все ок.
Теперь попробуем вставить запись:
insert into key (name,group_id) values ('ключ1',1)

выходит ошибка
mysql>ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`database/key`, CONSTRAINT `key_FK_2` FOREIGN KEY (`parent_id`) REFERENCES `key` (`id`) ON DELETE CASCADE)


Дело оказывается в `parent_id` int(11) default '0' , т.к. нет записи с id=0.
Правильнее сделать `parent_id` int(11) default NULL.

Мне кажется в propel при генерации из схемы можно сделать проверку на defaultValue, т.к. в купе с foreign key это уже ошибочно в большинстве случаев.

воскресенье, марта 22, 2009

Правильное расширение базовых классов Propel в symfony 1.2

Пусть наша модель называется SomeModel, тогда методом для извлечения объекта по строке(slug) будет:

class SomeModelPeer extends BaseSomeModelPeer
{
public static function retrieveBySlug($slug, PropelPDO $con = null){
if (null !== ($obj = SomeModelPeer::getInstanceFromPool((string) $slug))) {
return $obj;
}
if ($con === null) {
$con = Propel::getConnection(self::DATABASE_NAME, Propel::CONNECTION_READ);
}
$criteria = new Criteria();
self::addSelectColumns($criteria);
$criteria->add(self::SLUG,$slug);
$object = self::doSelectOne($criteria,$con);
if($object instanceof SomeModel){
self::addInstanceToPool($object);
}
return $object;
}
}

среда, марта 18, 2009

Как динамически изменить layout для всего application

Иногда нужно менять layout для всего приложения, на пример в празничные дни.
Но писать условия в каждом модуле - не красиво.
Поэтому можно сделать это заранее в фильтре.
В файле app/your_module/config/filters.yml добавь ваш фильтр:
rendering: ~
security: ~

# insert your own filters here

change_layuot:
class: changeLayoutFilter
param:

cache: ~
common: ~
execution: ~


Создайте класс changeLayoutFilter и положите его в lib/:
class changeLayoutFilter extends sfFilter
{
public function execute($filterChain)
{
$request = $this->getContext()->getRequest();
$user = $this->getContext()->getUser();
//устанавливаю layuot
if(isNY()){
sfConfig::set('symfony.view.'.
$this->getContext()->getModuleName().'_'.
$this->getContext()->getActionName().'_layout',
'your_layout');
}
// Execute next filter
$filterChain->execute();
}
}


isNY() - это ваше условие, 'your_layout' - ваш layout.

Кстати, почему я не использовал конструкцию по учебнику:
class changeLayoutFilter extends sfFilter
{
public function execute($filterChain)
{
// Execute this filter only once
if ($this->isFirstCall())
{
[... ...]
}
}
}

?
Потому что если будет использован forward в контроллере(sfAction), то
цепочка фильтров будет запущено заново и наш хак не сработает.
Посмотрите сами в дебагере, вкладка "log" как ваш фильтр будет запущен 2 раза.

вторник, марта 17, 2009

Валидатор телефонного номера в symfony 1.2

Телефонный номер вида: +7(495)1234567

/**
* sfValidatorTelephone
*
* @package
* @subpackage validator
* @author broderix
* @version
*/
class sfValidatorTelephone extends sfValidatorRegex
{
/**
* @see sfValidatorRegex
*/
protected function configure($options = array(), $messages = array())
{
parent::configure($options, $messages);

$this->setOption('pattern', '/^(\+\d\(\d+\)\d*)$/i');
}
}


чтобы заработал положить в lib/

суббота, марта 14, 2009

Упрощение работы с sfWidgetFormSelect в sfForm

Этим постом я начну серию статьёй по symfony 1.2

Иногда необходимо использовать ограниенные списки select в админе в symfony 1.2.
Но не хочется выбирать данные для sfWidgetFormSelect из массива или yaml файла.
Поэтому можно создать свой виджет sfWidgetFormYamlSelect:

class sfWidgetFormYamlSelect extends sfWidgetFormChoice
{
public function __construct($options = array(), $attributes = array())
{
$options['choices'] = new sfCallable(array($this, 'getChoices'));
parent::__construct($options, $attributes);
}
protected function configure($options = array(), $attributes = array())
{
addRequiredOption('form_name', null);
$this->addRequiredOption('field_name', null);
$this->addOption('add_empty', false);
parent::configure($options, $attributes);
}
public function getChoices()
{
$conf_array = sfConfig::get('app_'.$this->getOption('form_name'));
$choices = array();
if(false !== $this->getOption('add_empty')){
$choices[''] = true === $this->getOption('add_empty') ? '' :
$this->getOption('add_empty');
}
$choices = array_merge($choices,$conf_array[$this->getOption('field_name')]);
return $choices;
}
}


В файле config/app.yml (если его нет, то создайте) опишите ваши поля для форм:
all:
.form_settings:
my_form1:
type: { 'yes': 'да', 'no': 'нет }
my_form2:
status: { 0: 'новый', 1: 'подтвержденный', 2: 'редактируется' }



В самой форме это будет выглядеть так:

class MyFormForm1 extends BaseMyForm1Form
{
public function configure()
{
$this->setWidget('status' , new sfWidgetFormYamlSelect(array(
'form_name'=>'my_form1',
'field_name'=>'status'
)));
}
}





Так же это можно использовать в фильтрах.