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

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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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


Модерация и вывод комментариев

Ответ на комментарии и рекурсивный вывод

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

Отступы можно выводить в обычном цикле, но если отступы формируются с помощью блочных элементов и css, то проще использовать рекурсию.


Модуль для управления блогом

Блог включает управление комментариями, пользователями, rbac

Новости, сообщения, описания - это все статьи, а статьи - это блог. Блог является важной составляющей любого сайта.

Блог настолько важен, что самая популярная CMS на планете WordPress - это блог.

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


Как отправить email с сайта и не попасть в спам

Как указать реального отправителя отправляя сообщение с сайта

При создании сайта почти всегда предусматривают форму обратной связи.

Допустим у вас на сайте есть форма обратной связи и посетитель заполнил в ней поле Name и поле Email. Логично отправить сообщение с сайта от имени посетителя на адрес администратора сайта или, например, адрес отдела маркетинга.

Но так отправить сообщение не получится. При отправке сообщения с сайта, нужно чтобы отправитель имел тот-же домен, что и сайт.


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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

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


Как определить и вызвать событие

Пример события

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

В первом случае обработчик события один, во-втором их несколько. Но принцип один - наступило событие, надо действовать.


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

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

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


Пример работы с сессией

Если читатель хочет вспомнить, что просмотрено и прочитано

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


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

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

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

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


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

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

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

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


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

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

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

Например, можно устроить, что-то вроде надписи в столовой - "Уберите, пожалуйста, за собой посуду" (демо).


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

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

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


Доступ к таблицам Google

Рредактирование таблиц Google из приложения

В предыдущей статье уже была ссылка на работающий пример.

Следующим шагом стало выделение процесса аутентификации на сервисе Google в отдельный класс. А это, в свою очередь, позволило сделать расширение, немного упрощающее решение задачи.



Поиск



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

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

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

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


    Поделиться

Для обычной формы задать проверку может показаться не сложным common/models/Price.php.

public function rules()
{
  return [
    [
      [
        'type', 'fund_id', 'accommodation', 'food', 'treatment'
      ], 
      'unique', 
      'targetAttribute' => [
        'type', 'fund_id', 'accommodation', 'food', 'treatment'
      ], 
      'message' => \Yii::t('app', 'Duplicate entry'),
    ],

Такой вариант будет работать при добавлении новой строки. Но если понадобится поменять цену, в уже определенной строке, получим ошибку так как такая строка действительно уже существует.

Нужно свое правило. И лучше использовать класс. Обе проверки - на стороне сервера и на стороне клиента находятся в одном месте и легко контролируются.

Пример собственного валидатора

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

Если же изменился один или несколько ключевых атрибутов, то ищем новую комбинацию в таблице и добавляем ошибку, если такая комбинация существует common/components/UniqueValidator.php.

namespace common\components;
use yii\helpers\Url;
use yii\validators\Validator;
use common\models\Price;
/**
 * Check uniqueness by multiple values. 
 * If the values have not changed, we believe that the uniqueness is preserved. 
 * If one or more values have changed, we looking for a combination in the table. 
 * Error if exists.
 */
class UniqueValidator extends Validator
{
    public function init()
    {
        parent::init();
        $this->message = \Yii::t('app', 'Duplicate entry.');
    }
    public function validateAttribute($model, $attribute)
    {
        $old = $model->getOldAttributes();
        // Let's check if any key attributes have been changed.
        if($old && (
            $old['type'] != $model->type || 
            $old['fund_id'] != $model->fund_id || 
            $old['accommodation'] != $model->accommodation || 
            $old['food'] != $model->food || 
            $old['treatment'] != $model->treatment
        )) {
            // Is there any entity with a new values?
            if(Price::find()->where([
                'type' => $model->type, 
                'fund_id' => $model->fund_id, 
                'accommodation' => $model->accommodation, 
                'food' => $model->food, 
                'treatment' => $model->treatment,
            ])->exists()) {
                // An error pushed if entity exists.
                $model->addError($attribute, $this->message);
            }
        }
    }
    public function clientValidateAttribute($model, $attribute, $view)
    {
        $url = json_encode(Url::to(['price/entity-exists']));
        $old = json_encode($model->getOldAttributes());
        $message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        return <<<JS
var entity = {};
entity.url = $url;
entity.old = $old;
// Get new values from a form
entity.new = {
    type: $('#price-type').val(),
    fund_id: $('#price-fund_id').val(),
    accommodation: $('#price-accommodation').val(),
    food: $('#price-food').val(),
    treatment: $('#price-treatment').val()
};
// Let's check if any key attributes have been changed.
if(
    entity.new.type != entity.old.type ||
    entity.new.fund_id != entity.old.fund_id ||
    entity.new.accommodation != entity.old.accommodation ||
    entity.new.food != entity.old.food ||
    entity.new.treatment != entity.old.treatment
) {
    // Is there any entity with a new values?
    entity.exists = false;
    $.ajax({
        url: entity.url, 
        data: entity.new,
        dataType: "json",
        // Very important parameter. Without it entity.exists will be false in any case.
        async:false
    }).done(function(response) {
        entity.exists = response;
    });
    // An error pushed if entity exists.
    if(entity.exists)
        messages.push($message);
}
JS;
    }
}

Проверка на стороне клиента

Проверки на сервере и на стороне клиента имеют один алгоритм, но есть важное отличие в проверке на стороне клиента. Проверка происходит с помощью ajax-запроса и чертовски важно указать, что этот запрос не является асинхронным.

Ajax-запрос по умолчанию всегда асинхронный и, если не указать обратное, запрос будет отправлен и, не дожидаясь ответа, программа продолжит выполнение. Когда ответ готов часть done уже выполнена и entity.exists всегда получает false.

А почему сразу не использовать объект message в блоке done? Кажется это проще. Но в области видимости блока done нет объекта messages .

Controller

backend/controllers/PriceController.php

  public function actionEntityExists($type, $fund_id, $accommodation, $food, $treatment)
  {
    \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    return Price::find()->where([
      'type' => $type, 
      'fund_id' => $fund_id, 
      'accommodation' => $accommodation, 
      'food' => $food, 
      'treatment' => $treatment,
    ])->exists();
  }

Model

common/models/Price.php

  [['type', 'fund_id', 'accommodation', 'food', 'treatment'], 
    'common\components\UniqueValidator'],

Заключение

Написать собственный валидатор проще, чем кажется, но нужно быть тщательне́е при написании кода для проверки на стороне клиента. Чтобы не тратить время на поиск ошибки, нужно помнить две вещи о JavaScript - область видимости и асинхронный характер выполнения.

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

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