Каков правильный способ обработки повторяющихся ошибок в БД

#php #database #atk4

#php #База данных #atk4

Вопрос:

Я внедряю подписку в БД. Электронное письмо должно быть уникальным, поэтому у меня есть УНИКАЛЬНЫЙ индекс в базе данных. У меня есть этот код на моей странице init:

 $f = $p->add('MVCForm');
$f->setModel('Account',array('name','surname','email'));
$f->elements['Save']->setLabel('Subscribe');

if($f->isSubmitted())
{
    try
    {
         $f->update();

         //More useful code to execute when all is ok :)

    }
    catch(Exception_ValidityCheck $v)
    {
        //Handles validity constraint from the model
        $f->getElement($v->getField())->displayFieldError($v->getMessage());
    }
    catch(SQLException $se)
    {
        //If I'm here there is a problem with the db/query or a duplicate email
    }
}
  

Единственная информация SQLException — это форматированное HTML-сообщение, это единственный способ определить, связана ли ошибка с дублированной записью?

Комментарии:

1. Пожалуйста, посмотрите на fossies.org/unix/www/atk4-4.1.zip:a/atk4/lib/SQLException.php . ИМХО, я думаю, вы можете справиться с ними в целом. Сообщение об ошибке уже достаточно. Или вы можете сначала проверить, существует ли адрес электронной почты пользователя или нет.

2. Я посмотрел на источник, прежде чем спрашивать здесь… В случае дублирования недостаточно, мне нужно перенаправить пользователя на другую страницу. Может быть, SQLException может правильно настроить код ошибки, чтобы отразить ошибку DB, или отобразить ее более независимым от db способом?

3. @romaninsh Я постараюсь задать больше хороших вопросов! 😛 Я предлагаю в любом случае вставить mysql_errno в конструкцию SQLException, это может быть полезно даже для обнаружения других ситуаций сбоя для ограничения базы данных (например, ограничения внешних ключей).

Ответ №1:

Вот один из способов сделать это:

https://github.com/atk4/atk4-web/blob/master/lib/Model/ATK/User.php#L95

Хотя, если вы хотите выполнить пользовательское действие при дублировании, вам следует переместить GetBy за пределы модели, в логику страницы.

Комментарии:

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

2. Я планирую выделить некоторые исключения, которые не будут генерировать обратную трассировку, но если обратная трассировка не фиксируется в конструкторе, от этого мало пользы. «Exception_StopInit» является хорошим примером такого исключения, которое не требует обратного отслеживания.

Ответ №2:

Как предложил @Col, мы хотим использовать «insert ignore».

$form-> update() полагается на Model-> update(), который затем полагается на класс DSQL для построения запроса. DSQL поддерживает опции, но model сгенерирует для вас новый SQL.

Model-> dsql() создает запрос для модели. Он может работать с несколькими «экземплярами», где у каждого экземпляра есть отдельный запрос. Мне не особенно нравится этот подход, и я мог бы добавить новый класс модели, но пока он работает.

Посмотрите здесь: https://github.com/atk4/atk4-addons/blob/master/mvc/Model/MVCTable.php#L933

Функция insertRecord() вызывает dsql(‘modify’, false) несколько раз для построения запроса. Вероятно, самое простое, что вы могли бы сделать, это:

 function insertRecord($data=array()){
    $this->dsql('modify',false)->option('IGNORE');
    return parent::insertRecord($data);
}
  

после вставки записи Agile Toolkit автоматически попытается загрузить вновь добавленную запись. Однако он будет использовать соответствующие условия. Я думаю, что если запись игнорируется, вы все равно получите исключение. Если возможно, избегайте исключений в вашем рабочем процессе. Исключения требуют больших затрат процессора, поскольку они фиксируют обратную трассировку.

Единственным способом может быть полное переопределение insertRecord . Это не идеально, но это позволит вам выполнить один запрос, как вы хотите.

Я предпочитаю вручную проверять условие с помощью loadBy (или GetBy), потому что он учитывает условия модели и объединения. Например, у вас может быть soft delete в вашей таблице, и хотя MySQL key не позволит вам вводить, Model будет, и model-way также имеет больше смысла для бизнес-логики.

Комментарии:

1. Это хороший «взгляд под капот», но, похоже, он немного усложняет мой код… Может быть, исключения можно сделать менее ресурсоемкими, собирая данные обратной трассировки только по требованию, а не в конструкторе? ( github.com/atk4/atk4/blob/master/lib/BaseException.php#L37 )

2. Просто нужно определить пустые collectBasicData здесь github.com/atk4/atk4/blob/master/lib/Exception/InitError.php и подобные исключения.

Ответ №3:

Почему вы не хотите запускать simple select, чтобы проверить, отправлено ли электронное письмо?

Или сделайте так, чтобы он ВСТАВЛЯЛ IGNORE, а затем проверял affected_rows

Комментарии:

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

2. Я бы не сказал, что индекс должен избегать выбора. Хорошо, сделайте так, чтобы он ВСТАВЛЯЛ IGNORE, а затем проверял affected_rows, если вам не нравится select

3. Может быть хорошей идеей, но это должно быть правильно вписано в логику Agile Toolkit, поскольку вы не просто бросаете голые запросы. Я попытаюсь развить эту идею.

4. Этот способ может быть интересным, если atk может справиться с конфигурацией, и если результат может легко «всплыть»

5. Под «завернутым» я подразумеваю, что вы не просто пишете запросы в Agile Toolkit. Вам нужно вызвать правильный метод для правильного объекта, и если способа нет, то это должно быть правильно спланировано на объектно-ориентированном уровне. У вас есть вся мощь объектно-ориентированного программирования внутри вашей «модели», и способность переопределять любой метод помогает в подобных ситуациях, когда нужно что-то сделать помимо того, что закодировали разработчики ядра.