пятница, 28 октября 2016 г.

Nginx google font cache

Для одного проекта потребовалось кешировать google font на стороне сервера. Это особенно актуально при переходе на hhtp/2

proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=one:16m inactive=7d max_size=1024m;
proxy_temp_path /var/cache/nginx/temp;

server {
        listen 80;
        client_max_body_size 128M;
        server_name bb.local;
        root /home/vyachin/PhpstormProjects/bb-backend-develop/frontend/web;
        index index.php;
        resolver 8.8.8.8 8.8.4.4;

        location / {
                try_files $uri $uri/ /index.php?$args;
        }
        location ~ \.php$ {
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                include fastcgi_params;
                fastcgi_pass unix:/run/php/php7.0-fpm.sock;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param SERVER_NAME $host;
        }
        location /gfonts/ {
                proxy_pass https://fonts.googleapis.com/;
                proxy_ignore_headers Expires Cache-Control;
                proxy_set_header Accept-Encoding "";
                proxy_set_header Host fonts.googleapis.com;
                proxy_redirect off;

                proxy_cache one;
                proxy_cache_valid 200 301 302 304 5m;
                proxy_cache_key "$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri";
                proxy_hide_header "Set-Cookie";
                proxy_ignore_headers "Cache-Control" "Expires";
                proxy_cache_use_stale error timeout invalid_header http_500 http_502 http_503 http_504;

                sub_filter_once off;
                sub_filter_types text/css;
                sub_filter 'https://fonts.gstatic.com/' 'http://bb.local/gstatic/';
        }

        location /gstatic/ {
                proxy_pass https://fonts.gstatic.com/;
                proxy_ignore_headers Expires Cache-Control;
                proxy_set_header Accept-Encoding "";
                proxy_set_header Host fonts.gstatic.com;
                proxy_redirect off;

                proxy_cache one;
                proxy_cache_valid 200 301 302 304 5m;
                proxy_cache_key "$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri";
                proxy_hide_header "Set-Cookie";
                proxy_ignore_headers "Cache-Control" "Expires";
                proxy_cache_use_stale error timeout invalid_header http_500 http_502 http_503 http_504;
        }
}

вторник, 9 августа 2016 г.

Установка mailcatcher на ubuntu

Запускаем последовательно

apt-get install rubygems-integration ruby-dev build-essential libsqlite3-dev
gem install mime-types --version "< 3"
gem install mailcatcher --conservative

Работа с PostgreSQL

Настройка postgres на ubuntu

sudo apt-get install postgresql postgresql-contrib
sudo -u postgres psql

postgres=# CREATE DATABASE cabmodel;
CREATE DATABASE

postgres=# CREATE USER cabmodel WITH password 'password';
CREATE ROLE

postgres=# GRANT ALL privileges ON DATABASE cabmodel TO cabmodel;
GRANT

Про оптимизацию можно почитать тут http://postgresql.leopard.in.ua/

Настройка доступа к базе данных из yii2

        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'pgsql:host=localhost;dbname=cabmodel',
            'username' => 'cabmodel',
            'password' => 'password',
            'charset' => 'utf8',
        ],

пятница, 15 июля 2016 г.

Table storage engine for doesn't have this option

После обновления свой системы до ubuntu 16 обновился mysql до версии 5.7 Теперь при импорте дампа из старых версий mysql вылетает ошибка "Table storage engine for  doesn't have this option". Проблема была в ROW_FORMAT=FIXED в DDL создания таблицы. Решается так
sed -i 's/ROW_FORMAT=FIXED//g' backup.sql

вторник, 12 июля 2016 г.

Символ комментария в git

При работе с github символ # используется как префикс номера задачи, поэтому его нельзя использовать в комментариях к комитам. Поэтому меняем # на ; следующей командой
git config --global core.commentchar ';'

вторник, 31 мая 2016 г.

Наследование в javascript

var BaseModalMethods = {
    init : function ()
    {
        console.log('BaseModalMethods.init');
    },
    modal : function ()
    {
        this.beforeShow();
        console.log('BaseModalMethods.modal');
    }
};

function SimpleModalPlugin() {
    this.beforeShow = function() {
        console.log('SimpleModalPlugin.beforeShow');
    }
}

SimpleModalPlugin.prototype = BaseModalMethods;

var a = new SimpleModalPlugin();
a.modal();

Вывод:

SimpleModalPlugin.beforeShow
BaseModalMethods.modal

вторник, 5 апреля 2016 г.

Работа с моделями в Yii

Подход, который я вывел для себя, работает и с первой и со второй версии yii. И так Yii для работы с данными использует паттерн ActiveRecord - одна запись в таблице - один экземпляр класса. Но в жизни (реальных проектах) все сложнее. Редко когда одна форма работает только с одной таблицей. Из последних проектов простой пример - есть форма редактирования клиента, у клиента есть множество телефонов. Пользователю удобнее сразу указать все телефоны клиента в форме. В такой ситуации нам на помощь приходит сервисный класс. В Yii1 это будет наследник от CFormModel, Yii2 - Model. "Ручками" добавляем ему все свойства - тут удобнее добавить getter/setter для каждого свойства и внутри обращаться к ActiveRecord объектам. Незабываем добавлять правила валидации. При вызове метода save у сервисного класса "раскладываем" все по таблицам используя ActiveRecord модели и транзакцию. Вместо тысячи слов несколько строк кода

class Client extends CActiveRecord
{
    public function tableName()
    {
        return 'client';
    }

    public function rules()
    {
        return [['name', 'required'],];
    }
}

class Phone extends CActiveRecord
{
    public function tableName()
    {
        return 'phone';
    }

    public function rules()
    {
        return [
            ['number, client_id', 'required'],
            ['client_id', 'exist', 'className' => 'Client', 'attributeName' => 'id'],
        ];
    }
}

class ClientForm extends CFormModel
{
    /** @var Client */
    private $client;
    /** @var Phone */
    private $phones;

    public function rules()
    {
        return [['name', 'required'], ['phones', 'validatePhones']];
    }

    public function getName()
    {
        return $this->client->name;
    }

    public function setName($value)
    {
        $this->client->name = $value;
    }

    public function save()
    {
        if (!$this->validate()) {
            return false;
        }
        $tr = Yii::app()->db->beginTransaction();
        try {
            if (!$this->client->save(false)) {
                $this->addErrors($this->client->getErrors());
                throw new Exception;
            }
            $tr->commit();
            return true;
        } catch (Exception $e) {
            $tr->rollback();
        }
        return false;
    }
}

Тут реализованы не все методы, но с этого скелета уже можно начать. В одном проекте (который я сейчас делаю) у меня только getter/setter уже 20 шт и форма будет управлять 6 классами и их массивами. До того как я нашел такой "кашерный" вариант на просторах сети, я бы все засунул в контроллер. Больше я так не делаю.

Еще один довольно часто встречающийся кейс - хранение даты или любого форматированного поля (например номер телефона). Тут вариантов миллион и все они неправильные :-) А правильный мой. В базе дату можно хранить или в виде целого числа секунд, прошедших с 1 января 1970 года (вариант быстрый и удобный, если только не нужно потом делать расследование прямо в базе) или в виде типа date/datetime (удобно смотреть прямо в базе, но затратнее при хранении и выводе). Но когда мы начинаем работать через форму, пользователь не очень хочет видеть 2016-04-21 или 1459880043 в поле ввода, им подавай 21.04.2016. И так вместо тысячи слов код
Была моделька
class Client extends CActiveRecord
{
    public function tableName()
    {
        return 'client';
    }

    public function rules()
    {
        return [['name, birthday', 'required'],];
    }
}
добавляем фиктивное поле через набор getter/setter, специально для формы
/** * Class Client * @property string $birthday */
class Client extends CActiveRecord
{
    public function tableName()
    {
        return 'client';
    }

    public function rules()
    {
        return [['name,birthdayDate', 'required'], ['birthdayDate', 'date', 'format' => 'dd.MM.yyyy'],];
    }

    public function getBirthdayDate()
    {
        return Yii::app()->format->formatDate($this->birthday);
    }

    public function setBirthdayDate($value)
    {
        $this->birthday = date('Y-m-d', CDateTimeParser::parse($value, 'dd.MM.yyyy'));
    }
}
и в форме работаем с birthday через getter/setter

<div class="form-group">    
    <?= $form->labelEx($model, 'birthdayDate'); ?>
    <?= $form->dateField($model, 'birthdayDate', ['class' => 'input-sm']); ?>
    <?= $form->error($model, 'birthdayDate'); ?>
</div>

При правильной организации проекта - делается наследник от базовой модельки в отдельном модуле и эти getter/setter нигде больше не будут нам мешаться.

вторник, 22 марта 2016 г.

Чеклист по продвижению проекта

Уже 2 или 3 проекта у меня умерли в "зародыше" по причине отсутствия системного подхода к их развитию. К некоторым проектам я возвращаюсь через год или два и пытаюсь реанимировать их. Но сделать это сложнее чем "раскрутить" абсолютно новый проект. Здесь я опишу шаги которые необходимо сделать с самого начала.

Если вашего бизнеса нет в Интернете - значит вас нет в бизнесе. Билл Гейтс.
Сейчас это можно перефразировать как "если вас нет в google или яндекс - вас нет". Появиться в выдачи google или яндекса, к тому же в ТОПе - сложно. Я не специалист в СЕО, но единственное, что я понял - основной ресурс тут время. Все остальные приемы - это обман и хитрости. Но эти хитрости работают только сейчас, потому что завтра google подкрутит свой алгоритм и станет хуже чем было.

И так, появилась идея создать новый "супер" проект и срубить бабла или хотя бы получить какой-то опыт.

1. Регистрируем домен, можно даже бесплатный, переехать всегда можно. Обязательно получаем ssl сертификат. Я пользуюсь wosign.com заказывая сертификат на 3 года, но можно запариться с https://letsencrypt.org/ и обновляться раз в 3 месяца.
2. Делаем Яндекс обработчиком почты для нашего домена. Google за такое берет деньги, яндекс пока не жадный. Хотя аккаунт Яндекса нам все равно нужен, поэтому выбираем Яндекс.
3. Регистрируем основную почту в нашем домене и подключаем к ней  webmaster, метрику, Яндекс.Диск (будем хранить бэкапы базы и картинки). Лучше не подключать метрики в свою основную почту. Понадобится гостевой доступ к метрики - создаем отдельную почту в своем домене.
4. Регистрируем почту в Gmail, подключаем туда Google Webmaster, Google Analytics и Google +. Указываем что владелец аккаунта Google+ будет автором всех публикаций на нашем домене
Добавляем тег <link rel="author" href="https://plus.google.com/103741159886958565143"> на все страницы. Этот трюк важен для того чтобы вас заметил google.
5. Делаем заглушку на главную страницу сайта - сайт находится в разработке, можно картинкой. Пока этого достаточно.
6. Делаем новый блог на blogger на поддомене (blog.domain.tk) и периодически пишем новости о проекте. Для меня это самый неприятный момент, потому что я абсолютно не умею писать сочинения. Правда есть трюк - я пишу как умею и отдаю хорошим людям. Они исправляют и отличный пост в блог готов. Ссылку на пост публикуем в аккаунте Google+. "О чем писать ?" - о том что уже добавил в проект. Можно считать, что это документация по тому как пользоваться ресурсом. Можно представить, что это отчет перед "заказчиком", о том что ты сделал за неделю. Это отлично мотивирует, позволяет взглянуть на то, что уже сделано и что еще предстоит сделать.
7. Как говорят крутые сеошники - делаем сематическое ядро - по каким запросам нас смогут найти потенциальные пользователи.
8. Покупаем вечных ссылок штук 5-10 от доноров с PR > 2. Трюк в том что обычно у таких сайтов и ТИЦ хороший (обычно не значит всегда). Купить можно через sape, интерфейс у них отстой (с учетом сколько через них проходит бабла) однако выбор доноров самый большой.
9. Подключаем seopult или rookee на предмет мониторинга позиций сайта по ключевым запросам. Закупать временные ссылки через эти сервисы - это глупая идея, перестал им платить и ты тут же потерял ссылочную массу. Эти сервисы нам помогут только с рекомендациями по наполнению сайта. Они сравнят нас с теми кто уже находится в ТОП-е по интересующим нас запросам. Именно на этом этапе страница-заглушка превратиться в landing page с формой "подписаться на новости" и share кнопками соц сетей.
10. ЖДЕМ-С. С этим пунктом у меня хуже чем с п. 6. Ну не умею я ждать, хочется "сразу и все". Однако есть чем себя занять - делаем MVP.

Создание MVP будет идти "снаружи-внутрь" - постепенно добавляя новые разделы куда может забраться пользователь. Т.е. нет смысла добавлять в проект то до чего пользователь пока не сможет добраться с главной страницы, действуем постепенно.

Что дает такой подход. Google заметит сайт достаточно быстро - уже через неделю он его "прошерстит" целиком, но реально в выдачи появится он не скоро. Яндекс вообще тупой, ему нужно 2-3 раза пересчитать свой индекс (один пересчет это примерно 2-3 недели, МИНИМУМ), чтобы хоть на часть запросов вы начали появляться в выдачи. Получается у нас будет 2-3 месяца на создание MVP, пока мы появимся в выдачи.

Есть один момент - для поисковиков важная характеристика - поведение пользователя на сайте, сколько он проводит время, глубина просмотра. Получается у вас нет трафика из поисковика - потому что нет поведения пользователей на сайте. Это замкнутый круг. Но google и яндекс дают нам возможность разорвать это круг - реклама.

Периодически, по мере появления нового функционала я бы запускал рекламную кампанию на 1000 рублей так сказать для тестирования пользователей и получения обратной связи. Так можно тестировать - какой функционал важнее развивать в первую очередь - собираем события с метрики.

Несколько технических рекомендаций
1. Bootswatch набор тем для bootstrap, всем он уже надоел (bootstrap) а это будет чуть красивее
2. Ansible разворачивать проект только через этот инструмент, позволяет быстро переехать на другой хостинг, проверенно.
3. Yii2 - ну потому что он няшка
4. PHP7 - проверенно работает быстрее, а используя ansible установка не будет сложной
5. Микроразметка: хлебных крошки, рейтинги страницы (будут появляться звездочки в выдачи гугла), страницы (размечаем как Article если нет ничего подходящее). Тут хитрость в том, что в выдачи эта ссылка занимает больше места, а значит более заметна.
6. Grunt - настроить с самого начала этот инструмент и использовать его для frontend части.
7. Использовать шаблон yii2 advanced с самого начала. Если проект разрастется (тьфу-тьфу) - у нас уже все готово.
8. Тег description это то, что будет показываться в выдачи, это то что может привлечь посетителей, нужно подойти креативно. Если есть какие-то статистические характеристики - кол-во посетителей, кол-во постов - смело выводим в description.

9. Title. Для всех страниц должен быть уникален, если идет пагинация добавляем в заголовок суффикс "- страница 3". Лучше сразу следовать этому правилу чем потом исправлять уже попавшие страницы в индекс поисковиков.

В пользу моих мыслей https://megamozg.ru/company/take_the_cake/blog/25240/