GridView filter больше меньше <>=

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: GridView filter больше меньше <>=

Сообщение zelenin » 2014.11.25, 17:15

VaNnOrus писал(а): Админам не нужны удобства, только хардкор? :)
все зависит от задачи. как правило это грид - места нет, а при малом количестве места слайдер даст только неточное ограничение. а в админке как правило нужна точная выборка.

Аватара пользователя
VaNnOrus
Сообщения: 96
Зарегистрирован: 2014.10.09, 12:50

Re: GridView filter больше меньше <>=

Сообщение VaNnOrus » 2014.11.26, 11:36

Insolita писал(а):попробовала, но штука в том что при изменении select фильтр сразу начинает запрос фильтрации...
Можно попробовать задать validateOnBlur/...OnChange/...OnType false.

Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: GridView filter больше меньше <>=

Сообщение Insolita » 2014.11.26, 12:21

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

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: GridView filter больше меньше <>=

Сообщение zelenin » 2014.11.26, 13:08

Insolita писал(а):да можно, мне это в принципе то не нужно было, я чисто так особо не заморачиваясь набросала, на заметочку, вдруг когда понадобится
надо добавлять data-pjax="0" тем вещам, на которые не надо вешать pjax

lynicidn
Сообщения: 2221
Зарегистрирован: 2014.05.24, 15:12

Re: GridView filter больше меньше <>=

Сообщение lynicidn » 2014.11.26, 13:12

сделайте min и max, и необязательными, тогда знаки сравнения юзер не будет вводить, и вам при валидации меньше проблем

Shappy
Сообщения: 86
Зарегистрирован: 2013.09.19, 12:31

Re: GridView filter больше меньше <>=

Сообщение Shappy » 2014.11.26, 13:23

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

behavior:

Код: Выделить всё

<?php

namespace app\modules\admin\common\behaviors;

use yii;
use yii\base\Behavior;
use yii\db\ActiveRecord;
use yii\helpers\Html;

class AdminSignComparison extends Behavior
{
    /**
     * @var $comparisonAttributes array
     * Array of attributes that needed comparison
     */
    public $comparisonAttributes;

    /**
     * @return array
     */
    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_VALIDATE => 'setComparisonAttributes'
        ];
    }

    /**
     * Method before validate, delete comparison characters and sets needed value.
     * @return void
     */
    public function setComparisonAttributes()
    {
        if($this->owner->scenario != 'search')
            return;

        foreach($this->comparisonAttributes as $attribute => $settingsArray)
        {
            if(empty($this->owner->$attribute) || $settingsArray['type'] === false)
                continue;

            if($settingsArray['type'] === 'text')
            {
                $this->comparisonAttributes[$attribute]['comparison'] = '=';
                continue;
            }

            $this->setComparisonCharacters($attribute);
            if($settingsArray['type'] === 'datetime')
                $this->setDatetimeAttribute($attribute);
        }
    }

    /**
     * @param $attribute string
     * Function delete comparison characters and sets comparison characters into $comparisonAttributes
     */
    private function setComparisonCharacters($attribute)
    {
        switch(substr($this->owner->$attribute, 0, 1))
        {
            case '<':
                if(substr($this->owner->$attribute, 1, 1) == '=')
                {
                    $this->owner->$attribute = substr($this->owner->$attribute, 2);
                    $this->comparisonAttributes[$attribute]['comparison'] = '<=';
                }
                else if(substr($this->owner->$attribute, 1, 1) == '>')
                {
                    $this->owner->$attribute = substr($this->owner->$attribute, 2);
                    $this->comparisonAttributes[$attribute]['comparison'] = '<>';
                }
                else
                {
                    $this->owner->$attribute = substr($this->owner->$attribute, 1);
                    $this->comparisonAttributes[$attribute]['comparison'] = '<';
                }
                break;
            case '>':
                if(substr($this->owner->$attribute, 1, 1) == '=')
                {
                    $this->owner->$attribute = substr($this->owner->$attribute, 2);
                    $this->comparisonAttributes[$attribute]['comparison'] = '>=';
                }
                else
                {
                    $this->owner->$attribute = substr($this->owner->$attribute, 1);
                    $this->comparisonAttributes[$attribute]['comparison'] = '>';
                }
                break;
            default:
                if(substr($this->owner->$attribute, 0, 1) === '=')
                    $this->owner->$attribute = substr($this->owner->$attribute, 1);
                $this->comparisonAttributes[$attribute]['comparison'] = '=';
                break;
        }
    }

    /**
     * @param $attribute string
     * Function sets right comparison for datetime and call getComparisonDatetime() to get correct datetime
     */
    private function setDatetimeAttribute($attribute)
    {
        if($this->comparisonAttributes[$attribute]['comparison'] === '=')
            $this->comparisonAttributes[$attribute]['comparison'] = 'between';

        $attributeLength = strlen($this->owner->$attribute);

        if($attributeLength == 4)
        {
            if($this->comparisonAttributes[$attribute]['comparison'] === '=')
                $this->comparisonAttributes[$attribute]['secondAttribute'] = $this->owner->$attribute . '-12-31 23:59:59';
            $this->owner->$attribute = $this->getComparisonDatetime($attribute, 'year', $this->owner->$attribute);
        }
        elseif($attributeLength == 7)
        {
            if($this->comparisonAttributes[$attribute]['comparison'] === '=')
                $this->comparisonAttributes[$attribute]['secondAttribute'] = $this->owner->$attribute . '-' . date('t', strtotime($this->owner->$attribute)) . ' 23:59:59';
            $this->owner->$attribute = $this->getComparisonDatetime($attribute, 'month', $this->owner->$attribute);
        }
        elseif($attributeLength == 10)
        {
            if($this->comparisonAttributes[$attribute]['comparison'] === '=')
                $this->comparisonAttributes[$attribute]['secondAttribute'] = $this->owner->$attribute . ' 23:59:59';
            $this->owner->$attribute = $this->getComparisonDatetime($attribute, 'day', $this->owner->$attribute);
        }
        elseif($attributeLength == 13)
        {
            if($this->comparisonAttributes[$attribute]['comparison'] === '=')
                $this->comparisonAttributes[$attribute]['secondAttribute'] = $this->owner->$attribute . ':59:59';
            $this->owner->$attribute = $this->getComparisonDatetime($attribute, 'hour', $this->owner->$attribute);
        }
        elseif($attributeLength == 16)
        {
            if($this->comparisonAttributes[$attribute]['comparison'] === '=')
                $this->comparisonAttributes[$attribute]['secondAttribute'] = $this->owner->$attribute . ':59';
            $this->owner->$attribute = $this->getComparisonDatetime($attribute, 'minute', $this->owner->$attribute);
        }
        else
            $this->comparisonAttributes[$attribute]['comparison'] = '=';
    }

    /**
     * @param $attribute string
     * @param $period string
     * @param $value string
     * @return string
     *
     * Function sets right comparison for datetime and return correct datetime
     */
    private function getComparisonDatetime($attribute, $period, $value)
    {
        $comparisonDateTime = $value;
        $comparison = $this->comparisonAttributes[$attribute]['comparison'];
        switch($period)
        {
            case 'year':
                if($comparison === '<' || $comparison === '>=')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . '-01-01 00:00:00'));
                elseif($comparison === '<=' || $comparison === '>')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . '-12-31 23:59:59'));
                elseif($comparison === '<>' || $comparison === 'between')
                {

                    $this->comparisonAttributes[$attribute]['secondAttribute'] = date('Y-m-d H:i:s', strtotime($value . '-12-31 23:59:59'));
                    $this->comparisonAttributes[$attribute]['comparison'] = $comparison === '<>' ? 'not between' : 'between';
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . '-01-01 00:00:00'));
                }
                break;
            case 'month':
                if($comparison === '<' || $comparison === '>=')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . '-01 00:00:00'));
                elseif($comparison === '<=' || $comparison === '>')
                {
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . '-' . date('t', strtotime($value)) . ' 23:59:59'));
                }
                elseif($comparison === '<>' || $comparison === 'between')
                {
                    $this->comparisonAttributes[$attribute]['secondAttribute'] = date('Y-m-d H:i:s', strtotime($value . '-' . date('t', strtotime($value)).' 23:59:59'));
                    $this->comparisonAttributes[$attribute]['comparison'] = $comparison === '<>' ? 'not between' : 'between';
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . '-01 00:00:00'));
                }
                break;
            case 'day':
                if($comparison === '<' || $comparison === '>=')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ' 00:00:00'));
                elseif($comparison === '<=' || $comparison === '>')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ' 23:59:59'));
                elseif($comparison === '<>' || $comparison === 'between')
                {
                    $this->comparisonAttributes[$attribute]['secondAttribute'] = date('Y-m-d H:i:s', strtotime($value . ' 23:59:59'));
                    $this->comparisonAttributes[$attribute]['comparison'] = $comparison === '<>' ? 'not between' : 'between';
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ' 00:00:00'));
                }
                break;
            case 'hour':
                if($comparison === '<' || $comparison === '>=')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ':00:00'));
                elseif($comparison === '<=' || $comparison === '>')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ':59:59'));
                elseif($comparison === '<>' || $comparison === 'between')
                {
                    $this->comparisonAttributes[$attribute]['secondAttribute'] = date('Y-m-d H:i:s', strtotime($value . ':59:59'));
                    $this->comparisonAttributes[$attribute]['comparison'] = $comparison === '<>' ? 'not between' : 'between';
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ':00:00'));
                }
                break;
            case 'minute':
                if($comparison === '<' || $comparison === '>=')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ':00'));
                elseif($comparison === '<=' || $comparison === '>')
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ':59'));
                elseif($comparison === '<>' || $comparison === 'between')
                {
                    $this->comparisonAttributes[$attribute]['secondAttribute'] = date('Y-m-d H:i:s', strtotime($value . ':59'));
                    $this->comparisonAttributes[$attribute]['comparison'] = $comparison === '<>' ? 'not between' : 'between';
                    $comparisonDateTime = date('Y-m-d H:i:s', strtotime($value . ':00'));
                }
                break;
            default:
                $comparisonDateTime = $value;
                break;
        }
        return $comparisonDateTime;
    }

    /**
     * @param yii\db\ActiveQuery $query
     * @return yii\db\ActiveQuery
     *
     * Function return $query with all filters for search
     */
    public function getFiltersForSearch(yii\db\ActiveQuery $query)
    {
        foreach($this->comparisonAttributes as $attribute => $settingsArray)
        {
            if(empty($this->owner->$attribute) || $settingsArray['type'] === false)
                continue;

            if($this->comparisonAttributes[$attribute]['comparison'] === 'between' || $this->comparisonAttributes[$attribute]['comparison'] === 'not between')
                $query->andFilterWhere([
                    $this->comparisonAttributes[$attribute]['comparison'],
                    $this->owner->tableName() . '.' . $attribute,
                    $this->owner->$attribute,
                    $this->comparisonAttributes[$attribute]['secondAttribute']
                ]);
            else
                $query->andFilterWhere([
                    $this->comparisonAttributes[$attribute]['comparison'],
                    $this->owner->tableName() . '.' . $attribute,
                    $this->owner->$attribute
                ]);
        }
        return $query;
    }

    /**
     * @param $attribute string
     * @return string
     *
     * Function return textInput of filter
     */
    public function getFilter($attribute)
    {
        if(isset(Yii::$app->request->get($this->owner->formName(), null)[$attribute]))
            $value = Yii::$app->request->get($this->owner->formName(), null)[$attribute];
        else
            $value = null;
        return Html::textInput($this->owner->formName() . '['.$attribute.']', $value, ['class' => 'form-control']);
    }
}
Как использовать:
1. В моделе добавляем сценарий search. вписываем все поля для массового присваивания (в общем все поля что выводим и по которым есть фильтр):

Код: Выделить всё

    public function scenarios()
    {
        $scenarios = parent::scenarios();
        $scenarios['search'] = ['id', 'user_id', 'banner_place_id', 'start_datetime', 'end_datetime', 'cost', 'active', 'created_at', 'updated_at'];
        return $scenarios;
    }
2. Прикручиваем бехейвер:

Код: Выделить всё

public function behaviors()
    {
        return [
            'AdminSignComparison' => [
                'class' => AdminSignComparison::className(),
                'comparisonAttributes' => [
                    'id'=>['type'=>'numeric'],
                    'user_id'=>['type'=>'numeric'],
                    'banner_place_id'=>['type'=>'text'],
                    'start_datetime'=>['type'=>'datetime'],
                    'end_datetime'=>['type'=>'datetime'],
                    'cost'=>['type'=>'numeric'],
                    'created_at'=>['type'=>'datetime'],
                    'updated_at'=>['type'=>'datetime'],
                ]
            ]
        ];
    }
В comparisonAttributes вставляем аттрибуты которые должны использовать сортировку со знаками. И пишем каждому тип, numeric или datetime. В зависимости от поля.

3. В функцию search добавляем сценарий до валидации:

Код: Выделить всё

$this->setScenario('search');
И с запросом работаем так:

Код: Выделить всё

$query = $this->getBehavior('AdminSignComparison')->getFiltersForSearch($query);
        $query->andFilterWhere([
            'active_banners.active' => $this->active,
            'active_banners.banner_place_id' => $this->banner_place_id
        ]);
т.е. в моем случае первой строкой забираем условия по тем полям, для которых нужны сравнения.

Код: Выделить всё

$query = $this->getBehavior('AdminSignComparison')->getFiltersForSearch($query);
После этого руками указываю поля, для которых эти сравнения не нужны.

Код: Выделить всё

$query->andFilterWhere([
            'active_banners.active' => $this->active,
            'active_banners.banner_place_id' => $this->banner_place_id
        ]);
4. Поля во вьюхе выводим след. образом:

Код: Выделить всё

[
                'attribute'=>'id',
                'filter' => $searchModel->getBehavior('AdminSignComparison')->getFilter('id')
            ],
П.с. поля datetime храняться в Y-m-d H:i:s. Я их не переделывал, так и вывожу...
Все. В чем профит... Во-первых integer, double, и т.д. сравниваются <, <=, >, >=, <>, =. Так же сравнивать можно и поля с datetime типом. Так же можно писать например: 2014 (все поля за 2014 год), <2014 (все поля до 2014 года) и т.д. Так же можно писать и <2014-02 (Все меньше февраля 2014) и т.д. по дням часам минутам...
Сильно строго не судите, критика принимается...
Последний раз редактировалось Shappy 2014.11.26, 13:34, всего редактировалось 1 раз.

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: GridView filter больше меньше <>=

Сообщение zelenin » 2014.11.26, 13:27

всю работу с датами я бы вынес в отдельный метод и использовал бы DateTime с sub/add

Shappy
Сообщения: 86
Зарегистрирован: 2013.09.19, 12:31

Re: GridView filter больше меньше <>=

Сообщение Shappy » 2014.11.26, 13:42

zelenin писал(а):всю работу с датами я бы вынес в отдельный метод и использовал бы DateTime с sub/add
Вообще использовать DateTime наверно было бы разумней... А метод с датами жирным слишком был, разделил его на два...

Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: GridView filter больше меньше <>=

Сообщение Insolita » 2014.11.26, 14:23

zelenin писал(а):
Insolita писал(а):да можно, мне это в принципе то не нужно было, я чисто так особо не заморачиваясь набросала, на заметочку, вдруг когда понадобится
надо добавлять data-pjax="0" тем вещам, на которые не надо вешать pjax
так это для ссылок работает , а не для селектов, и там не pjax - там подвешенный скрипт от фильтра отрабатывает,

zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: GridView filter больше меньше <>=

Сообщение zelenin » 2014.11.26, 15:15

Insolita писал(а):
zelenin писал(а):
Insolita писал(а):да можно, мне это в принципе то не нужно было, я чисто так особо не заморачиваясь набросала, на заметочку, вдруг когда понадобится
надо добавлять data-pjax="0" тем вещам, на которые не надо вешать pjax
так это для ссылок работает , а не для селектов, и там не pjax - там подвешенный скрипт от фильтра отрабатывает,
у грида есть клиентские опции, где можно указать filterSelector, который по умолчанию "#$id input, #$id select"
Можно указать что-то типа "#$id input:not([data="not-filter"]), #$id select:not([data="not-filter"])"

Аватара пользователя
VaNnOrus
Сообщения: 96
Зарегистрирован: 2014.10.09, 12:50

Re: GridView filter больше меньше <>=

Сообщение VaNnOrus » 2014.11.26, 16:43

Shappy писал(а):П.с. поля datetime храняться в Y-m-d H:i:s. Я их не переделывал, так и вывожу...
Все. В чем профит... Во-первых integer, double, и т.д. сравниваются <, <=, >, >=, <>, =. Так же сравнивать можно и поля с datetime типом. Так же можно писать например: 2014 (все поля за 2014 год), <2014 (все поля до 2014 года) и т.д. Так же можно писать и <2014-02 (Все меньше февраля 2014) и т.д. по дням часам минутам...
Сильно строго не судите, критика принимается...
Критика уже была - юзабилити страдает.
Если уж совсем лень прикручивать ползунки - два инпута "от" и "до" - для пользователя и то лучше будет.
Дата так же от и до, но обязательно человеческими календарями. Если это сайт не для программистов - Ваши 2014-12-21 добрая половина пользователей просто не поймет, потому что так не принято писать даты в жизни.

Shappy
Сообщения: 86
Зарегистрирован: 2013.09.19, 12:31

Re: GridView filter больше меньше <>=

Сообщение Shappy » 2014.11.26, 17:25

VaNnOrus писал(а):
Shappy писал(а):П.с. поля datetime храняться в Y-m-d H:i:s. Я их не переделывал, так и вывожу...
Все. В чем профит... Во-первых integer, double, и т.д. сравниваются <, <=, >, >=, <>, =. Так же сравнивать можно и поля с datetime типом. Так же можно писать например: 2014 (все поля за 2014 год), <2014 (все поля до 2014 года) и т.д. Так же можно писать и <2014-02 (Все меньше февраля 2014) и т.д. по дням часам минутам...
Сильно строго не судите, критика принимается...
Критика уже была - юзабилити страдает.
Если уж совсем лень прикручивать ползунки - два инпута "от" и "до" - для пользователя и то лучше будет.
Дата так же от и до, но обязательно человеческими календарями. Если это сайт не для программистов - Ваши 2014-12-21 добрая половина пользователей просто не поймет, потому что так не принято писать даты в жизни.
Да, согласен что было бы удобней с человеческими датами... Но это надо будет переделывать работу с датой... Может потом займусь этим, сделаю все по человечески... А на счет ползунков... Ну даже не знаю... Мне кажется и так хорошо:)) Можно конечно тоже что-нибудь придумать... Доделаю как будет время... Спасибо...

Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: GridView filter больше меньше <>=

Сообщение Insolita » 2014.11.26, 20:50

Shappy писал(а):

Код: Выделить всё

 if($this->owner->scenario != 'search')
            return;
Shappy писал(а): Как использовать:
1. В моделе добавляем сценарий search.
Сценарии нужные поисковые самому бы в behavior подставлять, причём чтоб массивом,а не один конкретно заданный

Shappy
Сообщения: 86
Зарегистрирован: 2013.09.19, 12:31

Re: GridView filter больше меньше <>=

Сообщение Shappy » 2014.11.26, 21:58

Insolita писал(а):
Shappy писал(а):

Код: Выделить всё

 if($this->owner->scenario != 'search')
            return;
Shappy писал(а): Как использовать:
1. В моделе добавляем сценарий search.
Сценарии нужные поисковые самому бы в behavior подставлять, причём чтоб массивом,а не один конкретно заданный
Честно говоря не понял... Я понял так: чтобы behavior выполнялся только при определенных (нескольких) сценариях, которые укажет пользователь. Нам этот behavior нужен только при GridView, это всего один метод, больше он нигде не используется... Сделать это не проблема, а где это может понадобиться?

Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Re: GridView filter больше меньше <>=

Сообщение Insolita » 2014.11.26, 22:27

ну вообще-то на одну модель может быть и несколько разных GridView и вариантов поисков

Shappy
Сообщения: 86
Зарегистрирован: 2013.09.19, 12:31

Re: GridView filter больше меньше <>=

Сообщение Shappy » 2014.11.26, 22:57

Insolita писал(а):ну вообще-то на одну модель может быть и несколько разных GridView и вариантов поисков
Понял, спасибо, учту...

Ответить