vorst.ru - Статьи о задачах возникающих при создании сайта и их решении
Статьи помеченные Yii2

Группировка email сообщений для сокращения трафика

Использование событий очереди

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

Пользователь может быть подписан на несколько товаров, по каждому товару может произойти несколько событий. Сообщений может быть слишком много и надо их группировать, чтобы уменьшить трафик.


Как удалить не используемые CSS правила

Очистка и сжатие CSS с помощью Gulp

Сейчас много шаблонов на базе bootstrap. На сайте, из всего разнообразия стилей, предлагаемых шаблоном, используется порой 10-20%. От балласта можно избавиться с помощью on-line ресурсов. Например uncss-online. Но этот подход трудоемок.

Надежнее использовать утилиту. Задание создается один раз и при любых изменениях в проекте достаточно выполнить одну или пару команд.


Как написать валидатор

Пример валидатора для серверной и клиентской частей

Есть таблица цен на номера в пансионате. Цена зависит от различных параметров - номера, типа ценника, типа питания, варианта размещения, наличия или отсутствия лечения. Цена, для определенной комбинации значений этих параметров, может быть только одна.

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


Интерфейс для полнотекстового поиска

Использование ElasticSearch во frontend

На создание ElasticSearch индекса понадобилось совсем не много усилий, как видно из предыдущей статьи. Чтобы сделать индекс доступным конечному пользователю, усилий понадобиться не многим больше.

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


Как создать индекс ElasticSearch

Полнотекстовый поиск

Один парень написал: "Нужно сделать поиск слов в таблице "articles" по столбцу "content". В результатах поиска сначала выводить те, которые содержат наибольшее количество слов в статье и далее по мере уменьшения."

Мысль летит) Но ясно, что нужен полнотекстовый поиск.


Рефакторинг кода авторизации

Использование аккаунта социальной сети для авторизации

Авторизация с помощью социальной сети не должна зависеть от типа аккаунта. В предыдущем посте используется код, где в конструкторе классa common/components/SocialContact.php выполняется оператор switch. При добавлении обработчика для новой социальной сети, придется дописывать код, при этом меняя класс.

Перепишем конструктор так, чтобы класс не нужно было менять и он не зависел от типов социальных сетей.


Как подключить авторизацию через аккаунт

OAuth2 авторизация с использованием аккаунта

Допустим, что вы уже зарегистрированы в известной социальной сети. То есть вы уже сообщили о себе, все, что считали нужным. Очевидно вам не хочется проходить процедуру еще раз на другом ресурсе.

Авторизация с помощью социальной сети позволяет избежать повторного заполнения формы регистрации. При этом защищенность вашего аккаунта в социальной сети никак не страдает. Вы просто подтверждаете, что вы это вы. Как подключить эту услугу к проекту используя расширение yiisoft/yii2-authclient?


Как определить правило для RBAC

Выполнять действие или нет

В предыдущей статье были определены две роли - Админ и Автор, как в обычном блоге. А обычно блог используется при создании любого сайта. Для ролей Админ и Автор были определены разрешения.

Если вы Админ, то у вас есть общее разрешение create. Оно дает право добавлять данные к любым моделям. Если вы Автор, то такое разрешение давать не стоит. Автор не может, например, добавить новую рубрику.


Определение разрешений на доступ и ролей для пользователей

Задание доступа только к определенным действиям

Разработка бекенда сайта часто подразумевает ограничение прав при редактировании контента.

Ролевая модель имеет иерархию. Сначала прав не много, потом чуть больше, наконец доступно все. Права (или разрешения) - это константы, которые связаны с конкретной ролью. Если за ролью закреплено разрешение, то действие может быть выполнено.


Как подключить RBAC

Подключение ролевого доступа

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

Именно распределение прав для групп пользователей и будет рассмотрено в паре-тройке статей. В качестве примера будет использоваться всего две роли.


Как отсортировать картинки мышкой

Сортировка строк мышкой

Оставили комментарий в конце января: "Напишите, пожалуйста, статью, как массово загружать изображения через ajax с возможностью сортировки перетаскиванием."

Речь о расширении yii2-uploader, которое может загружать, сжимать, обрезать картинки для любой модели. Я писал о нем уже пару раз.

На тот момент этой функциональности не было. Собирался я долго. Наконец добавил требуемое. Теперь спешу написать несколько слов о процессе. Демо (при редактировании).


Пример загрузки контента без перезагрузки страницы

Динамическая загрузка контента

Блог - это лента из заголовков статей и нескольких строчек из них. Пусть так и останется, когда посетитель решит прочитать содержимое выбранной статьи. То есть вся лента останется и добавится контент выбранной статьи.


Как добавить атрибут к загруженному файлу

Добавление описания к файлу

Иногда важно определить дополнительную информацию для загружаемого файла. Например это может быть описание. В расширении yii2-uploader возможность добавить описание к файлу уже предусмотрена.

Кроме описания может быть добавлено и любое другое свойство. В предыдущем примере использовалось свойство when типа radio с двумя значениями - "до" и "после". Как добавить новое свойство?


Как сделать галерею фотографий "До и После"

Показ фотографий до и после момента времени

Представьте, что вы продаете курс похудания. Вам нужно наглядно представить результат вашего курса. Фото "До" и "После", что может быть лучшей иллюстрацией?

Как и в предыдущем примере примем, что участников ограниченное количество. Например 4. Забудем про похудающих - слишком скучно. Будем "работать" с кинозвездами. Для каждого можно загрузить только 4 фотографии. Таким образом всего 16 фото. Если загружены все 16 и кому-то захочется добавить "свои" фото, то придется что-то удалить. Демо.


Делаем демо для загрузки фото

Загрузка жестко определенного количества файлов

Чтобы показать, как работает загрузка изображений, нужно разрешить загружать изображения на свой сайт. Но, в этом случае, дисковое пространство может быть быстро исчерпано и нужно как-то удалять оставленный посетителями "мусор".

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


Редактирование и валидация модальной формы

Использование pop-up формы

Бывает, что количество полей в таблице не слишком велико и редактировать в модальном окне проще и нагляднее. Что необходимо, чтобы организовать стандартный набор операций - CReate, Update, Delete?


Как работает аутентификация OAuth2

OAuth2 аутентификация на примере Google

Если у вас есть аккаунт в Google, то вы наверняка пользовались документами. Например, электронными таблицами.

Доступ к таблицам разрешен только вам и тем, кому вы предоставите такое право. Причем доступ возможен не только из Google.

Можно просматривать список таблиц и редактировать выбранную таблицу оставаясь в рамках своего приложения. Примерно так - http://sample.vorst.ru/googl


Как устроена структура данных Nested Set

Как сделать рубрикатор

Рубрикатор, как средство поиска интересных статей, имеет свои недостатки - трудно придумать иерархию, названия рубрик, трудно потом решить к какой конкретно рубрике относится статья. "Но, так принято" - скажите вы и будете правы.



Поиск



Группировка email сообщений для сокращения трафика

Использование событий очереди

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

Пользователь может быть подписан на несколько товаров, по каждому товару может произойти несколько событий. Сообщений может быть слишком много и надо их группировать, чтобы уменьшить трафик.


    Поделиться

Итак у нас есть модель Good - описание товара, с ценой и фотографиями. Определим события, которые требует Заказчик.

class Good extends ActiveRecord
{
    const EVENT_NEW_PICTURE         = 'new_picture';
    const EVENT_DESCRIPTION_CHANGED = 'desciption_changed';
    const EVENT_PRICE_REDUCTION     = 'price_reduction';
    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        $this->on(self::EVENT_NEW_PICTURE, function($event) {
            $this->notifyObservers($event);
        });
        $this->on(self::EVENT_DESCRIPTION_CHANGED, function($event) {
            $this->notifyObservers($event);
        });
        $this->on(self::EVENT_PRICE_REDUCTION, function($event) {
            $this->notifyObservers($event);
        });
    }
    /**
     * Get observers
     * 
     * @return \yii\db\ActiveRecord
     */
    public function getObservers()
    {
        return $this->hasMany(Wishlist::className(), [
            'good_id' => 'id',
        ]);
    }
    /**
     * Notify observers
     *     
     * @param \yii\base\Event $event
     */
    public function notifyObservers($event)
    {
        foreach($this->observers as $observer) {
            $this->keepNotification([
                'user_id' => $observer->user_id,
                'good_id' => $this->id,
                'event'   => $event->name,
            ]);
        }
    }
    /**
     * Keep information about an event in a list
     * 
     * @param array $data
     */
    public function keepNotification($data)
    {
        $file = fopen(Yii::$app->queue->path . '/data.csv', 'a');
        fwrite($file, "{$data['user_id']},{$data['good_id']},{$data['event']}\n");
        fclose($file);
    }

Обрабатываются события в два этапа. Сначала события сохраняются в общем списке (используется обычный файл). События сохраняются одно за другим до момента старта воркера (обработчика очереди).

php yii queue/run -v --color

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

class PrepareNotificationBehavior extends Behavior
{
    public function events()
    {
        return [
            Queue::EVENT_WORKER_START => 'prepareNotification',
        ];
    }
    public function prepareNotification($event)
    {
        $queue = $event->sender;
        if (!file_exists($queue->path . '/data.csv'))
            // no events
            return;
        $jobs = [];
        // spread out all events by users, then by goods
        // for example user_id = 13, good_id = 1001, then 
        // $jobs[13][1001] = ['new_picture', 'price_reduction'];
        foreach(file($queue->path . '/data.csv') as $line) {
            if (count($a = str_getcsv($line)) == 3) {
                list($user_id, $good_id, $event_name) = $a;
                if (!isset($jobs[$user_id = (int) $user_id]))
                    $jobs[$user_id] = [];
                if (!isset($jobs[$user_id][$good_id = (int) $good_id]))
                    $jobs[$user_id][$good_id] = [];
                if (!isset($jobs[$user_id][$good_id][$event_name = trim($event_name)]))
                    $jobs[$user_id][$good_id][] = $event_name;
            }
        };
        // create jobs, one for each users
        foreach($jobs as $user_id => $goods) {
            $queue->push(new SendNotificationJob([
                'user_id' => $user_id,
                'goods'   => $goods,
            ]));
        }
        // delete event list
        unlink($queue->path . '/data.csv');
    }
}

Обработчик надо подключить к очереди через config.

    'components' => [ 
        'queue' => [ 
            'class' => \yii\queue\file\Queue::class, 
            'path' => '@console/runtime/queue', 
            'as prep' => \common\behaviors\PrepareNotificationBehavior::class, 
        ],

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

class SendNotificationJob extends BaseObject implements \yii\queue\JobInterface
{
    public $user_id;
    public $goods;
    /**
     * Send email to observer
     */
    public function execute($queue)
    {
        // find User
        if (!($user = User::findOne(['id' => $this->user_id])))
            throw new NotFoundHttpException('User with ID - ' . 
            $this->user_id . ' not exists.');
        // generate and save token, for unsubscribe link
        if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
            $user->generatePasswordResetToken();
            $user->save();
        }
        // find goods
        $models = Goods::find()
            ->where(['in', 'id', array_keys($this->goods)])
            ->all();
        // send email
        if ($this->goods && $models)
            Yii::$app->mailer
                ->compose(['html' => 'notification-html'], [
                    'user'   => $user, 
                    'models' => $models, 
                    'goods'  => $this->goods, 
                ])
                ->setFrom([Yii::$app->params['email']['support'] => 
                    (Yii::$app->name . ' robot')])
                ->setTo($user->email)
                ->setSubject($subject)
                ->send();
    }
}

Заключение

Запуск вокера предполагается выполнять только командой run руками или через cron. Если в приложении используется очередь и для других целей, то стоит определить дополнительную очередь в конфигурации.

Оставьте комментарий

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