Как указать ошибку для конкретного поля, которое является массивом ?

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

Есть форма, которая содержит следующие поля:

title
meta_title
meta_description
и тп.

Я сделал форму, которая генерируется на основе подключенных языков, тоесть такие поля превращаются в массивы:

title[ru]
meta_title[ru]
meta_description [ru]

title[en]
meta_title[en]
meta_description [en]
и тп.


Выглядит это вот таким образом:

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

                 <div class="row">
                            <div class="col-md-4 col-md-4 col-sm-4 col-xs-6 col-lg-4"><?= $form->field($formModel, 'meta_title['.$lang['code'].']')->textInput(['maxlength' => true]) ?></div>
                            <div class="col-md-4 col-md-4 col-sm-4 col-xs-6 col-lg-4"><?= $form->field($formModel, 'meta_description['.$lang['code'].']')->textInput(['maxlength' => true]) ?></div>
                            <div class="col-md-4 col-md-4 col-sm-4 col-xs-6 col-lg-4"><?= $form->field($formModel, 'meta_keywords['.$lang['code'].']')->textInput(['maxlength' => true]) ?></div>
                        </div>
Все отлично работает, за исключением, когда речь идет о валидации. Тоесть если в русском языке к примеру допущена ошибка, то Yii помечает ошибку как глобально на все поле. Тоесть, вместо title-ru он говорит, что ошибка в поле title, тем самым указывая, все все переводы указаны неверно, даже если это не так.




Все происходит вот из-за этого кода:

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

public static function getAttributeName($attribute)
    {
        if (preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
            return $matches[2];
        } else {
            throw new InvalidParamException('Attribute name must contain word characters only.');
        }
    } 

Тоесть он просто всегда удаляет все, что в [] и не дает возможность валидировать только определенное поле в массиве. Что с этим делать ?
andrei.obuhovski
Сообщения: 610
Зарегистрирован: 2015.07.16, 10:50

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение andrei.obuhovski »

Сдается мне, что тут нужно было так делать:

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

$form->field($model, "[".$lang['code']."]meta_title")
Последний раз редактировалось andrei.obuhovski 2016.02.29, 18:02, всего редактировалось 1 раз.
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение rak »

варианты навскидку:
1. делать переводы отдельными моделями, тогда можно использовать код вида

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

<?= $form->field($langModels['en'], '[en]meta_title')->textInput() ?>
2. добавить дополнительные поля в модель, валидировать их, а перед сохранением сохранять в нужные поля бд
3. использовать готовое поведение, например, вот это https://github.com/OmgDef/yii2-multilingual-behavior
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

andrei.obuhovski писал(а):Сдается мне, что тут нужно было так делать:

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

$form->field($model, "[".$lang['code']."]meta_title") 
В таком случае придется создавать свойства в модели по имени кодов языка, а заранее мы не знаем сколько их будет и какие они.
rak писал(а):варианты навскидку:
1. делать переводы отдельными моделями, тогда можно использовать код вида

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

<?= $form->field($langModels['en'], '[en]meta_title')->textInput() ?>
2. добавить дополнительные поля в модель, валидировать их, а перед сохранением сохранять в нужные поля бд
3. использовать готовое поведение, например, вот это https://github.com/OmgDef/yii2-multilingual-behavior
1 - хороший вариант. Но тогда нужно будет писать мануал по php и yii2 в админке и возможность создавать модели для заказчика через текстовый редактор.

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

3 - я не использую подобные расширения, это весьма сомнительные реализации. С Yii2 нужно быть особо осторожным.


А зачем было так усложнять ? Интересно, что по поводу этого скажем Александр Макаров ?
andrei.obuhovski
Сообщения: 610
Зарегистрирован: 2015.07.16, 10:50

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение andrei.obuhovski »

nepster писал(а):
andrei.obuhovski писал(а):Сдается мне, что тут нужно было так делать:

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

$form->field($model, "[".$lang['code']."]meta_title") 
В таком случае придется создавать свойства в модели по имени кодов языка, а заранее мы не знаем сколько их будет и какие они.
По аналогии как здесь:
https://github.com/yiisoft/yii2/blob/ma ... r-input.md
$form->field($setting, "[$index]value")
Только вместо $index, ваш код языка.
Foreach'у какая разница что перебирать.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

Либо я понял смысл и мне такое к сожалению не подходит, либо я не понял идею =(.

У меня нет работы с ActivRecord, вообще с ним на прямую лучше не работать. У меня есть модель (base Model), в которой я получаю все данные, там я их валидирую привожу к нужному формату и только потом сохраняю через актив рекорд. Тоесть по сути мне нужно сейчас при валидации сделать что-то такое:

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

$this->addError('[en]title', 'Возникла ошибка именно с языком EN'); 
И в любом случае та нехорошая регулярка обрезает все, что есть в [].
andrei.obuhovski
Сообщения: 610
Зарегистрирован: 2015.07.16, 10:50

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение andrei.obuhovski »

Если я правильно понял, то у вас одни и те же поля повторяются N количество раз. (где N - количество языков)

Тогда вам нужно создать одну модель с этими полями. А выводить, и заполнять, и валидировать через foreach (или loadMultiple и validateMultiple). При этом в качестве ключей вам нужно указать коды ваших языков(или их айдишнники).

Т.е. логика примерно такая:
При выводе:
1) Получаем массив языков
2) Перебираем их в цикле, и делаем новый массив из FormModel. В качестве ключей указываем коды языков.
3) Выводим в цикле форму. $form->field($formModel, "[$lang]meta_title")

При отправке:
1) Model::LoadMultiple()
2) Model::ValidateMultiple()
3) Сохраняем в цикле
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

Честно говоря я не совсем понимаю зачем *Multiple у меня нет нескольких моделей и валидация мне нужна кастомная. Да и в этом случае тот злой метод вчерашнего удалит скобки и оставит только title. В результате ошибки будут везде.


По факту я просто в одной модели хочу поработать с языками и потом что-то сделать, мне не нужно по модели на каждый язык. Это походу косяк yii.
Nerf
Сообщения: 780
Зарегистрирован: 2015.01.29, 00:37

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение Nerf »

Переопределите yii\widgets\ActiveField. Как минимум error(). А потом еще...
Про js валидацию, наверное, стоит забыть. Получите снежный ком проблем.

andrei.obuhovski дело говорит.
Сделайте на каждый язык модель(не суть AR или нет) с нужными вам полями.
Controller:

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

$models = [];
$langCodes = ['ru', 'en']; // из параметров, БД...
foreach($langCodes as $code) {
    $models[$code] = new YouModel([
        'lang' => $code,
    ]);
}

if (Model::loadMultiple($models, Yii::$app->request->post()) && Model::validateMultiple($models)) {
    foreach ($models as $model) {
        // $model->save(false);
        // ваша логика сохранения
    }
    return $this->redirect('index');
}

return $this->render('update', ['models' => $models]);
View:

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

use yii\helpers\Html;
use yii\widgets\ActiveForm;

$form = ActiveForm::begin();

foreach ($models as $langCode => $model) { // вывод всех полей модели, или продублируйте foreach, чтобы вывести заголовок 1, заголовок 2...
    echo $form->field($model, "[$langCode]title")->label($model->getTranslatedLabel('title')); // label() опционально
    echo $form->field($model, "[$langCode]meta_title")->label($model->getTranslatedLabel('meta_title'));
    // ...
}

ActiveForm::end();
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

Сделайте на каждый язык модель(не суть AR или нет) с нужными вам полями.
Nerf писал(а):Переопределите yii\widgets\ActiveField. Как минимум error(). А потом еще...
Про js валидацию, наверное, стоит забыть. Получите снежный ком проблем.

andrei.obuhovski дело говорит.
Сделайте на каждый язык модель(не суть AR или нет) с нужными вам полями.
Controller:

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

$models = [];
$langCodes = ['ru', 'en']; // из параметров, БД...
foreach($langCodes as $code) {
    $models[$code] = new YouModel([
        'lang' => $code,
    ]);
}

if (Model::loadMultiple($models, Yii::$app->request->post()) && Model::validateMultiple($models)) {
    foreach ($models as $model) {
        // $model->save(false);
        // ваша логика сохранения
    }
    return $this->redirect('index');
}

return $this->render('update', ['models' => $models]);
View:

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

use yii\helpers\Html;
use yii\widgets\ActiveForm;

$form = ActiveForm::begin();

foreach ($models as $langCode => $model) { // вывод всех полей модели, или продублируйте foreach, чтобы вывести заголовок 1, заголовок 2...
    echo $form->field($model, "[$langCode]title")->label($model->getTranslatedLabel('title')); // label() опционально
    echo $form->field($model, "[$langCode]meta_title")->label($model->getTranslatedLabel('meta_title'));
    // ...
}

ActiveForm::end();
Я ж говорю, к сожалению за ранее неизвестно какие будут языки, сколько их, какой по умолчанию и тп. Все задает админ.
JS валидация это такое, она и так не всегда работает, например в случае с exist unique и тп.

Мне кажется это жирный косяк АктивФорма. А точнее даже хелпера Html::error(). Потому, как если убрать ту регулярку вроде пробелмы пропадают. И тут либо что-то мы упускаем, либо косячелло.
Последний раз редактировалось nepster 2016.03.05, 13:38, всего редактировалось 1 раз.
andrei.obuhovski
Сообщения: 610
Зарегистрирован: 2015.07.16, 10:50

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение andrei.obuhovski »

nepster писал(а): Я ж говорю, к сожалению за ранее неизвестно какие будут языки, сколько их, какой по умолчанию и тп. Все задает админ.
После того как админ задал, оно же хранится где-то. Вот оттуда и берите.
nepster писал(а): JS валидация это такое, она и так не всегда работает, например в случае с exist unique и тп.
Ajax-валидацией решается.
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение rak »

nepster писал(а): 3 - я не использую подобные расширения, это весьма сомнительные реализации. С Yii2 нужно быть особо осторожным.
угу, а потом тот, кто будет поддерживать этот код будет вспоминать предыдущего разработчика не злым тихим словом за очередной велосипед с квадратными колесами
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

Вот как раз наоборот скорее всего. У меня вся логика инкапсуляция в разных моделях, а не вешаются глобальные непонятные поведения на всю модель. Я уже это проходит. Если у вас несколько сценариев и все завязано на одной форме, то оптом геморроя будет прилично. Если ваше приложение хоть чуть-чуть больше блога, с актив рекордом нужно быть очень осторожным.

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

Как только нужно сделать что-то кастомное, виджеты yii сыпятся.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

В общем пока реализовал в стиле Yii2 "не нравится? переопредели":

В бутстрап файле (только для админки, ну понятно, что для продакшина человек в здравом уме виджеты yii не использует):

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

        Yii::$container->set('yii\widgets\ActiveForm', [
            'fieldClass' => 'app\components\backend\ActiveField',
        ]);
 
Привет DI. Отличная штука, когда пришло озарение зачем она.


Далее переопределяем 2 метода

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

    public function error($options = [])
    public function begin()
 
На первый взгляд работает. Но если кому-то нужно будет, такую штуку не используйте, пока не понятно к чему это может привести.
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение rak »

nepster писал(а):Вот как раз наоборот скорее всего. У меня вся логика инкапсуляция в разных моделях, а не вешаются глобальные непонятные поведения на всю модель. Я уже это проходит. Если у вас несколько сценариев и все завязано на одной форме, то оптом геморроя будет прилично. Если ваше приложение хоть чуть-чуть больше блога, с актив рекордом нужно быть очень осторожным.

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

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

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

но вот для чего городить огород для задач, которые уже 100 раз решались ранее - не понятно
Nerf
Сообщения: 780
Зарегистрирован: 2015.01.29, 00:37

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение Nerf »

Just fo lulz...
Вот как раз наоборот скорее всего. У меня вся логика инкапсуляция в разных моделях
Как раз наоборот наоборот. Логика в разных моделях звучит многообещающе... Почитайте про SOLID.
Если ваше приложение хоть чуть-чуть больше блога, с актив рекордом нужно быть очень осторожным.
О Боже, какой мужчина! ActiveRecord - это ORM по одноименному шаблону проектирования. Что же там такого опасного?
но проблема в том, что мне нужна логика валидации. Например если вы указали дефолтный язык, то для друго-го валидация на required будет необязательна. В свою очередь, если вы указали другой язык, то тоже самое для дефолтного.
И почему для этого не работают rules()?
Как только нужно сделать что-то кастомное, виджеты yii сыпятся.
Потому что вы пытаетесь их использовать не по назначению.
Мне кажется это жирный косяк АктивФорма. А точнее даже хелпера Html::error(). Потому, как если убрать ту регулярку вроде пробелмы пропадают. И тут либо что-то мы упускаем, либо косячелло.
Если убрать ту регулярку, часть функционала не будет работать.
только для админки, ну понятно, что для продакшина человек в здравом уме виджеты yii не использует
Продакшена? Я использую виджеты yii и в бекенде, и в фронтенде. И вроде на здравый ум не так часто жалуются :D
Yii::$container->set('yii\widgets\ActiveForm', [
'fieldClass' => 'app\components\backend\ActiveField',
]);
Только учтите, что в других местах будет использоваться этот класс(в зависимости где вы это указали). Если используете ActiveForm в других местах и явно не задаете fieldClass, то что-то у вас скорее всего поломается. Не проще указать в конфиге ActiveForm? С DIC как раз нужно быть осторожней.

По непонятным причинам, обрабатывая набор некоторых сущностей, вы пытаетесь слепить их вместе и ругаетесь, что решение вам не подходит, а поэтому неправильное. Ну, на ошибках учимся.
nepster
Сообщения: 838
Зарегистрирован: 2013.01.02, 03:35

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение nepster »

rak писал(а):
nepster писал(а):Вот как раз наоборот скорее всего. У меня вся логика инкапсуляция в разных моделях, а не вешаются глобальные непонятные поведения на всю модель. Я уже это проходит. Если у вас несколько сценариев и все завязано на одной форме, то оптом геморроя будет прилично. Если ваше приложение хоть чуть-чуть больше блога, с актив рекордом нужно быть очень осторожным.

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

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

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

но вот для чего городить огород для задач, которые уже 100 раз решались ранее - не понятно
В бд все впорядке. Отдельная табилца, ключи, все дела. У вас просто не было задач посложнее.



Nerf писал(а):Just fo lulz...
Вот как раз наоборот скорее всего. У меня вся логика инкапсуляция в разных моделях
Как раз наоборот наоборот. Логика в разных моделях звучит многообещающе... Почитайте про SOLID.
Если ваше приложение хоть чуть-чуть больше блога, с актив рекордом нужно быть очень осторожным.
О Боже, какой мужчина! ActiveRecord - это ORM по одноименному шаблону проектирования. Что же там такого опасного?
но проблема в том, что мне нужна логика валидации. Например если вы указали дефолтный язык, то для друго-го валидация на required будет необязательна. В свою очередь, если вы указали другой язык, то тоже самое для дефолтного.
И почему для этого не работают rules()?
Как только нужно сделать что-то кастомное, виджеты yii сыпятся.
Потому что вы пытаетесь их использовать не по назначению.
Мне кажется это жирный косяк АктивФорма. А точнее даже хелпера Html::error(). Потому, как если убрать ту регулярку вроде пробелмы пропадают. И тут либо что-то мы упускаем, либо косячелло.
Если убрать ту регулярку, часть функционала не будет работать.
только для админки, ну понятно, что для продакшина человек в здравом уме виджеты yii не использует
Продакшена? Я использую виджеты yii и в бекенде, и в фронтенде. И вроде на здравый ум не так часто жалуются :D
Yii::$container->set('yii\widgets\ActiveForm', [
'fieldClass' => 'app\components\backend\ActiveField',
]);
Только учтите, что в других местах будет использоваться этот класс(в зависимости где вы это указали). Если используете ActiveForm в других местах и явно не задаете fieldClass, то что-то у вас скорее всего поломается. Не проще указать в конфиге ActiveForm? С DIC как раз нужно быть осторожней.

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

1) SOLID никакого отношения не имеет к Yii2. В Yii2 почти каждый класс нарушет принцип единой ответственности. И как говорил Александр, можно обойтись без SOLID и Yii2 это некий баланс между безнесом и суровым ооп. Поэтому писать по SOLID в рамках Yii2 крайне тяжело, а иногда и невозможно.

2) ActiveRecord - на больших проектах это очень опасная штука. Я уже 500 раз описывал ее проблемы. Все у кого выростает проект, рано или позжно понимают, что неправильно приготовленный ActiveRecord - это полный ад.

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

4) "Я использую виджеты yii и в бекенде, и в фронтенде. И вроде на здравый ум не так часто жалуются :D" - потому, что вы не решаете "суровые" задачи. Для прототипов и стандартны штуковен это то что нужно.

5) и по поводу ActiveForm. Мне такое поведение нужно только в админке, на фронте я не рискую больше использовать актив форм. Используя DI контейнер мы просто подменяем данные помолчанию. Тоесть в остальных случаях будет стандартное значение. Это делается для того, чтобы каждой форме не прописывать конфиг.

На самом деле DI это очень крутая штука, которой не пользуются в Yii2, потому что так исторически сложилось.
Nerf
Сообщения: 780
Зарегистрирован: 2015.01.29, 00:37

Re: Как указать ошибку для конкретного поля, которое является массивом ?

Сообщение Nerf »

1) SOLID никакого отношения не имеет к Yii2. В Yii2 почти каждый класс нарушет принцип единой ответственности. И как говорил Александр, можно обойтись без SOLID и Yii2 это некий баланс между безнесом и суровым ооп. Поэтому писать по SOLID в рамках Yii2 крайне тяжело, а иногда и невозможно.
Это всего лишь набор принципов. Я не настаиваю, что нужно строго следовать им везде. Но стараться придерживаться их, наверное, все же неплохо. На самом деле, не зная что в действительности представляют ваши объекты, я могу быть не прав, но со стороны кажется, что вы делаете "велосипед с квадратными колесами".
ActiveRecord - на больших проектах это очень опасная штука. Я уже 500 раз описывал ее проблемы. Все у кого выростает проект, рано или позжно понимают, что неправильно приготовленный ActiveRecord - это полный ад.
Очень интересно услышать доводы. Наверное, "неправильно приготовленный ActiveRecord - это полный ад" - ключевое. Что не есть проблема AR.
4) "Я использую виджеты yii и в бекенде, и в фронтенде. И вроде на здравый ум не так часто жалуются :D" - потому, что вы не решаете "суровые" задачи. Для прототипов и стандартны штуковен это то что нужно.
Выпейте немного "антиЧСВина". Что же в них такого ужасного, опасного и т.п., чтобы их не использовать?
Используя DI контейнер мы просто подменяем данные помолчанию. Тоесть в остальных случаях будет стандартное значение. Это делается для того, чтобы каждой форме не прописывать конфиг.
В остальных случаях будет то, что вы указали в DIC, если явно не указано что использовать. Т.е. в других виджетах ActiveForm будет использоваться переопределенный класс.
На самом деле DI это очень крутая штука, которой не пользуются в Yii2, потому что так исторически сложилось.
Не используете вы => все? В "ядре" Yii используется.
Ответить