Ярлыки

.htaccess (4) тестирование (8) шаблоны проектирования (3) css (5) Debian (6) docker (2) Doctrine2 (6) Git (6) html (4) java (6) javascript (13) jquery (11) LFS (3) linux (23) mac os (4) mod_rewrite (2) MSSQL (4) MySQL (18) ORM Doctrine (17) patterns (3) PDO (3) perl (7) PHP (64) PHPUnit (8) Python (15) SEO (2) Silex (1) SimpleXML (1) SQL (14) ssh (4) Ubuntu (24) Yii1 (1) Zend Framework (19) ZendFramework2 (8)

пятница, 23 марта 2012 г.

Zend Framework. Композитный элемент формы на примере номера телефона.

Объект элемента формы library/App/Form/Element/Phone.php

class App_Form_Element_Phone extends Zend_Form_Element_Xhtml {

        public $helper = 'phoneElement';
        protected $countryCode;
        protected $providerCode;
        protected $number;

        public function setCountryCode($num) {
                $this->countryCode = $this->filter($num);
                return $this;
        }

        public function setProviderCode($num) {
               $this->providerCode = $this->filter($num);
               return $this;
        }
 
        public function setNumber($num) {
               $this->number = $this->filter($num);
               return $this;
        }
 
        // Примитивная фильтрация данных, вырезаем все кроме цифр
        protected function filter($num) {
              return preg_replace('/[^\d]/', '', $num);
        }

        public function __construct($spec, $options = null) {
             parent::__construct($spec, $options);
             // Можно не удалять
             $this->removeDecorator('HtmlTag'); 
        }

        public function setValue($value) {
             if (is_array($value) && isset($value['country_code'],
                          $value['provider_code'], $value['number'])) {
                     $this->setCountryCode($value['country_code'])
                         ->setProviderCode($value['provider_code'])
                         ->setNumber($value['number']);
                    }
        }
 
        public function getValue() {
             if(! $this->countryCode || ! $this->providerCode
                  || ! $this->number)
                 return false; // Благодаря этому срабатывает валидатор NotEmpty
             return array(
                 'country_code' => $this->countryCode,
                 'provider_code' => $this->providerCode,
                 'number' => $this->number
             );
        }

}

Помощник вида для элемента library/App/View/Helper/PhoneElement.php

class App_View_Helper_PhoneElement extends Zend_View_Helper_FormElement {

     protected $html = '';

     public function phoneElement($name, $value = null, $attribs = null) {
         $providerCode = $countryCode = $number = '';
         // Формирует элемент html - текстовое поле,
         // стандартный помощник вида
         $helper = new Zend_View_Helper_FormText();
         $helper->setView($this->view);
         $countryCode = isset($value['country_code'])
             ? $value['country_code'] : '7';
         $providerCode = isset($value['provider_code'])
             ? $value['provider_code'] : '';
         $number = isset($value['number'])
             ? $value['number'] : '';
         // Формируем html-код элемента
         $this->html .= '<div class="phone-plus">+</div>' 
                   . $helper->formText($name 
                   . '[country_code]', $countryCode, array('size' => 3,
                                                         'maxlength' => 3));
         $this->html .= $helper->formText($name 
                   . '[provider_code]', $providerCode, array('size' => 5,
                                                         'maxlength' => 5));
         $this->html .= $helper->formText($name 
                   . '[number]', $number, array('size' => 7, 'maxlength' => 7));
         $this->html .= '<div class="clear"></div>';
         return $this->html;
     }

}

Применение элемента в форме


...
$phone = new App_Form_Element_Phone('phone');
$phone->setLabel('Телефон +7(XXX)XXXXXXX')->setRequired();
$phone->addValidator('NotEmpty', true, array(
     'messages' => array('isEmpty' => 'Необходимо ввести номер телефона')));
$this->addElement($phone);
...

четверг, 22 марта 2012 г.

PHP. Публикация вакансий для поискового сервиса Trovit.

Однажды потребовалось генерировать xml-поток объявлений о работе для поискового сервиса http://www.trovit.com/. Сервис довольно популярен в штатах. Позволяет размещать объявления о работе, недвижимости и продаже автомобилей. Так как задача ограничивалась объявлениями о работе а человек я ленивый, то любой желающий может запросто дописать пару очень коротких классов для работы с другими разделами. Написал очень маленькую библиотеку.

Тут собственно требования к потоку http://about.trovit.com/your-ads-on-trovit/russia/feed-ru-rabota/

 Страница валидатора http://about.trovit.com/your-ads-on-trovit/russia/feed-ru-rabota/

Клиентский код, так примерно это может выглядеть

require_once 'Trovit/Ad.php';
require_once 'Trovit/JobsAd.php';
require_once 'Trovit/Feed.php';
require_once 'Trovit/JobsFeed.php';

// Некий класс вакансии (объявления о работе)
class Vacancy {

   public $id;
   public $url;
   public $title;
   public $content;
   public $date;
   public $company;

}

// Некий адаптер клиентских данных,
// расширяющий абстрактный класс объявления о работе
class Ad extends Trovit_JobsAd {

   protected $vacancy;

   public function __construct(Vacancy $vacancy) {
      $this->vacancy = $vacancy;
   }

   public function getId() {
      return $this->vacancy->id;
   }

   public function getUrl() {
      return $this->vacancy->url;
   }

   public function getTitle() {
      return $this->vacancy->title;
   }

   public function getContent() {
      return $this->vacancy->content;
   }

   public function getDate() {
      return $this->vacancy->date;
   }

   public function getCompany() {
      return $this->vacancy->company;
   }
 
   public function getPostcode() {
      return '099900';
   }

   ...

   // Опциональные методы определены в классе-родителе

}

$vacancy = new Vacancy();
$vacancy->id = 1;
$vacancy->url = 'http://example.com';
$vacancy->title = 'Tester Vacancy';
$vacancy->content = 'Lorem ipsum dolor sit amet, consectetur 
 adipisicing elit, sed do eiusmod tempor incididunt ut labore
 et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
 exercitation ullamco laboris nisi ut aliquip ex ea commodo
 consequat. Duis aute irure dolor in reprehenderit in
 voluptate velit esse cillum dolore eu fugiat nulla pariatur.
 Excepteur sint occaecat cupidatat non proident,
 sunt in culpa qui officia deserunt
 mollit anim id est laborum.';
$vacancy->company = 'Tester Company';
$vacancy->date = '22/03/2012 17:30:00';

$vacancy1 = new Vacancy();
$vacancy1->id = 2;
$vacancy1->url = 'http://example1.com';
$vacancy1->title = 'Tester Vacancy';
$vacancy1->content = 'Lorem ipsum dolor sit amet, consectetur
 adipisicing elit, sed do eiusmod tempor incididunt ut labore
 et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
 exercitation ullamco laboris nisi ut aliquip ex ea commodo
 consequat. Duis aute irure dolor in reprehenderit
 in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
 Excepteur sint occaecat cupidatat non proident,
 sunt in culpa qui officia deserunt mollit anim id est laborum.';
$vacancy1->company = 'Tester Company';
$vacancy1->date = '22/03/2012 17:30:00'; //ДД/ММ/ГГГГ ЧЧ:ММ:CC

$ad = new Ad($vacancy);
$ad1 = new Ad($vacancy1);

$feed = new Trovit_JobsFeed();
$feed->addAd($ad);
$feed->addAd($ad1);
file_put_contents('trovit.xml', $feed->getXML());

Проходит валидацию



Скачать архив с библиотекой Post to Trovit.com with PHP easy

среда, 21 марта 2012 г.

Zend Framework. Валидатор длинны строки.

Пример:
$pwd = new Zend_Form_Element_Password('password');
$pwd->setLabel('Пароль не менее 6 символов *');
...
$stringLength = new Zend_Validate_StringLength(6);
$stringLength->setMessage(
    'Пароль слишком короткий; минимальная длинна %min% символов',
    Zend_Validate_StringLength::TOO_SHORT);
$pwd->addValidator($stringLength);

четверг, 8 марта 2012 г.

Nginx. Проксирование запросов.

Потребовалось иметь хост, который проксирует запросы к разным версиям сайта.
Например, мы делаем запрос http://mysiteproxy/index/action1 и выдается страница с
http://site1/index/action1, а если делаем запрос http://mysiteproxy/index/action2 -
получаем http://site2/index/action2. Что-то вроде этого. Пример для ubuntu.

Настраиваем nginx и apache2.

Apache будет слушать порт 8080.
$ sudo nano /etc/apache2/ports.conf

...
NameVirtualHost *:8080
Listen 8080
...
Настриваем проксирование запросов к apache. Создаем файл
/etc/nginx/conf.d/proxy.conf
proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
client_header_buffer_size 64k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffer_size   16k;
proxy_buffers       32   16k;
proxy_busy_buffers_size 64k;
Редактируем настройки хоста по-умолчанию /etc/nginx/sites-available/default.
Все запросы шлем на localhost:8080, по-умолчанию nginx висит на порту 80. Не забываем потом поместить конфиг. файл в sites-enabled. Я обычно делаю ссылку.
server {
     #listen   80; ## listen for ipv4; this line is default and implied
     #listen   [::]:80 default ipv6only=on; ## listen for ipv6

     root /usr/share/nginx/www;
     index index.html index.htm;

     # Make site accessible from http://localhost/
     server_name localhost;

     location / {
         # First attempt to serve request as file, then
         # as directory, then fall back to index.html
         try_files $uri $uri/ /index.html;
  
         proxy_pass http://127.0.0.1:8080;
     }
}

Перезапускаем сервера
sudo /etc/init.d/apache2 restart
sudo /etc/init.d/nginx restart

Настраиваем хост-прокси

Создаем конфигурационный файл хоста
sudo nano /etc/nginx/sites-avaliable/mysiteproxy.conf

server {
        listen   80;
        server_name  mysiteproxy;

        access_log  /var/log/nginx/access.log;

        location = / {
               include         /etc/nginx/site1.proxy.conf;
        }

        location / {
               include         /etc/nginx/site2.proxy.conf;
        }

        location ~* \.(jpeg|jpg|gif|png|css|js|pdf|txt|tar)$ {
               root /var/www/site1/;
        }
}
Тут директивы location указывают как будут проксироваться запросы (подробно в документации nginx).
В данном случае главная будет с site1, внутренние страницы - site2.
Создаем два файла конфигурации прокси
sudo nano /etc/nginx/site1.proxy.conf


proxy_pass       http://127.0.0.1:8080/;
proxy_set_header        Host            site1;

proxy_redirect          http://site1/  /;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           32 4k;
Аналогично для второго хоста.
Перезапускаем nginx
sudo /etc/init.d/nginx restart
Если не настраиваем DNS, то прописываем хосты в /etc/hosts.

Doctrine2. Использование репозиториев.

При работе с Doctrine2 все манипуляции с базой производим через Doctrine\ORM\EntityManager.
Простой способ получить запись из базы данных.
$user = $em->find('Entities\User', $id);
Очень часто нам приходится работать с репозиториями объектов (что-то общее с коллекциями Doctrine1).
$user = $em->getRepository('Entities\User')->find($id);
В данном случае менеджер проверяет репозитории сопоставленные с данным классом и если таких нет,
то использует класс репозитория по-умолчанию (Doctrine\ORM\EntityRepository). Он содержит
следующие базовые методы:
public function findAll()
public function findBy(array $criteria)
public function findOneBy(array $criteria)
Часто этого бывает недостаточно. Класс EntityRepository содержит метод
public function createQueryBuilder($alias)
Мы можем наследовать базовый класс и создавать собственные репозитории
namespace Repositories;
 
use Doctrine\ORM\EntityRepository;
use Entities;
 
class UserRepository extends EntityRepository
{
    public function finderMethod($arguments){
        // My custom query etc
    }
}
Мы должны указать, что надо использовать новый репозиторий в классе модели
namespace Entities;
 
/** @Entity(repositoryClass="Repositories\UserRepository")
 *  @Table(name="dealers")
 */
class User
...
Теперь мы можем это использовать
$users = $em->getRepository('Entities\User')->finderMethod($arguments);

среда, 7 марта 2012 г.

Zend Framework. Помощник контроллера.

1. Сообщаем helper broker, путь к каталогу с помощниками.
Это можно сделать, например, в bootstrap:
  Zend_Controller_Action_HelperBroker::addPath(
        APPLICATION_PATH .'/controllers/helpers');
2. Пишем хелпер application/controllers/helpers/Myhelper.php:

class Zend_Controller_Action_Helper_Myhelper extends
                Zend_Controller_Action_Helper_Abstract
{
    function direct()
    {
        return __METHOD__;
    }

    function another()
    {
        return __METHOD__;
    }
}
3. Вызываем хелпер в контроллере:
class IndexController extends Zend_Controller_Action 
{
    public function indexAction() 
    {
        echo $this->_helper->myhelper();
        echo $this->_helper->myhelper->another();
        die;
    }
}