OmgDef / yii2-multilingual-behavior

Yii2 port of the yii-multilingual-behavior.
146 stars 60 forks source link

Conflict with unique validator #4

Closed matperez closed 9 years ago

matperez commented 9 years ago

Looks like it's trying to apply UniqueValidator for virtual localized field.

I got following error trying to create new entry with localized attribute (name) that requires uniquenes

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name_ru' in 'where clause' The SQL being executed was: SELECT * FROM airport WHERE name_ru='Внуково' LIMIT 2

Saving is successful if i comment [['name'], 'unique'], validator in model rules.

matperez commented 9 years ago

Добавлю по русски. Есть модель airport с полем name. Для этого поля должен использоваться UniqueValidator. Если к нему же прикрутить MultilingualBehavior, то при сохранении модели получаю ошибку, что поле в целевой таблице не найдено. Естественно, поле name_ru и не должно присутствовать в таблице airport. Судя по логам он пытается вызвать UniqueValidator для поля name_ru, хотя ничего такого в правилах модели не прописано. Alt Text

OmgDef commented 9 years ago

Попробуйте кастомизировать unique валидатор

matperez commented 9 years ago

Здравствуйте!

Подскажите как именно это можно сделать? Написать свой? Я так понимаю, он не должен вызывать валидатор в таком виде. Этого атрибута ведь не существует в базе. Даже в таблице с переводами он прописан как просто name...

CREATE TABLE `airportLang` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `airportId` int(11) NOT NULL,
  `language` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `description` text COLLATE utf8_unicode_ci,
  PRIMARY KEY (`id`),
  KEY `idx_airportLang_airport` (`airportId`),
  CONSTRAINT `idx_airportLang_airport` FOREIGN KEY (`airportId`) REFERENCES `airport` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
OmgDef commented 9 years ago

Валидаторы динамически применяются для всех переводов. Соотвественно, возникает проблема с уникальностью мультиязычных полей. Атрибут и не должен существовать в БД. Для реализации мультиязычности добавляются динамические атрибуты модели в виде attribute + _ + lang. Так как стандартный валидатор позволяет лишь дополнять запрос к БД, вам придется сделать свою реализацию, например в виде inline валидатора. Немного удивлен, что мультиязычные поля проверяют на уникальность...

matperez commented 9 years ago

Так я и не хочу проверять виртуальный атрибут на уникальность, его проверка сама собой возникает, если валидатор навешан на исходный атрибут (локализуемый). т.е. если в модели есть атрибут name и для него прописана проверка на уникальность, дополнительно в той же таблице базы проверяется уникальность виртуального атрибута name_en/name_ru, хотя никаких правил для него нет. Я так понял, что behavior где-то внутри метода configure пытается применить к виртуальным полям валидаторы исходных и как раз в случае unique validator это работает не совсем корректно.

matperez commented 9 years ago

Если топорно, то можно добавить исключение в код, который навешивает валидаторы, тогда все работает, но можно наверное как-то более красиво все сделать.

                        if ($rule[1] === 'unique') {
                            // do nothing
                        } elseif ($rule[1] !== 'required' || $this->forceOverwrite) {
                            if (isset($rule['skipOnEmpty']) && !$rule['skipOnEmpty'])
                                $rule['skipOnEmpty'] = !$this->forceOverwrite;
                            $validators[] = Validator::createValidator($rule[1], $owner, $attribute . '_' . $language, array_slice($rule, 2));
                        }  elseif ($rule[1] === 'required') {
                            //We add a safe rule in case the attribute has only a 'required' validation rule assigned
                            //and forceOverWrite == false
                            $validators[] = Validator::createValidator('safe', $owner, $attribute . '_' . $language, array_slice($rule, 2));
                        }
OmgDef commented 9 years ago

Пофиксил в релизе 1.0.2