Symfony2 преобразователь данных, валидатор и сообщение об ошибке - PullRequest
2 голосов
/ 22 февраля 2012

Я задал этот вопрос и обнаружил, что мы не можем получить сообщение об ошибке, выданное DataTransformer (по словам единственного пользователя, который ответил, возможно, это возможно, я не знаю).

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

<?php

class Thread
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToMany(targetEntity="My\UserBundle\Entity\User")
     * @ORM\JoinTable(name="messaging_thread_user")
     */
    private $participants;

    // other fields, getters, setters, etc
}

Для создания потока я хочу, чтобы пользователь указал имена пользователей участников в текстовой области, разделенные знаком "\ n". И я хочу, чтобы, если одно или несколько из указанных имен пользователей не существовало, отображается сообщение с именами пользователей, которых не существует . Например, «Пользователи titi, tata и toto не существуют».

Для этого я создал DataTransformer, который преобразует необработанный текст в текстовой области в коллекцию ArrayCollection, содержащую экземпляры пользователей. Так как я не могу получить сообщение об ошибке, предоставленное этим DataTransformer (такой позор! Это действительно невозможно?), Я не проверяю существование каждого имени пользователя в DataTransformer, но в Валидаторе.

Вот DataTransformer, который преобразует разделенный \ n список пользователей в ArrayCollection (так, чтобы с DataBinding все было в порядке):

<?php

public function reverseTransform($val)
{
    if (empty($val)) {
        return null;
    }

    $return = new ArrayCollection();

    // Extract usernames in an array from the raw text
    $val = str_replace("\r\n", "\n", trim($val));
    $usernames = explode("\n", $val);
    array_map('trim', $usernames);

    foreach ($usernames as $username) {
        $user = new User();
        $user->setUsername($username);
        if (!$return->contains($user)) {
            $return->add($user);
        }
    }

    return $return;
}

А вот мой валидатор:

<?php

public function isValid($value, Constraint $constraint)
{
    $repo = $this->em->getRepository('MyUserBundle:User');
    $notValidUsernames = array();

    foreach ($value as $user) {
        $username = $user->getUsername();
        if (!($user = $repo->findOneByUsername($username))) {
            $notValidUsernames[] = $username;
        }
    }

    if (count($notValidUsernames) == 0) {
        return true;
    }

    // At least one username is not ok here

    // Create the list of usernames separated by commas
    $list = '';
    $i = 1;

    foreach ($notValidUsernames as $username) {
        if ($i < count($notValidUsernames)) {
            $list .= $username;
            if ($i < count($notValidUsernames) - 1) {
                $list .= ', ';
            }
        }
        $i++;
    }

    $this->setMessage(
            $this->translator->transChoice(
                'form.error.participant_not_found',
                count($notValidUsernames),
                array(
                    '%usernames%' => $list,
                    '%last_username%' => end($notValidUsernames)
                )
            )
    );

    return false;
}

Эта текущая реализация выглядит некрасиво. Я хорошо вижу сообщение об ошибке, но пользователи в ArrayCollection, возвращаемые DataTransformer, не синхронизируются с Doctrine.

У меня есть два вопроса:

  • Может ли мой валидатор изменить значение, указанное в параметре? Чтобы я мог заменить простые экземпляры User в ArrayCollection, возвращенные DataTransformer, на экземпляры, извлеченные из базы данных?
  • Есть ли простой и элегантный способ делать то, что я делаю?

Полагаю, самый простой способ сделать это - получить сообщение об ошибке, выданное DataTransformer. В кулинарной книге они выдают это исключение: throw new TransformationFailedException(sprintf('An issue with number %s does not exist!', $val));, если бы я мог поместить список несуществующих имен пользователей в сообщение об ошибке, было бы круто.

Спасибо!

1 Ответ

2 голосов
/ 22 февраля 2012

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

Ваш код может быть значительно упрощен. Вы имеете дело только с именами пользователей. Нет необходимости использовать объекты или коллекции массивов.

public function reverseTransform($val)
{
    if (empty($val)) { return null; }


    // Extract usernames in an array from the raw text
    // $val = str_replace("\r\n", "\n", trim($val));
    $usernames = explode("\n", $val);
    array_map('trim', $usernames);

    // No real need to check for dups here
    return $usernames;
}

Валидатор:

public function isValid($userNames, Constraint $constraint)
{
    $repo = $this->em->getRepository('SkepinUserBundle:User');
    $notValidUsernames = array();

    foreach ($userNames as $userName) 
    {
      if (!($user = $repo->findOneByUsername($username))) 
        {
            $notValidUsernames[$userName] = $userName; // Takes care of dups
        }
    }

    if (count($notValidUsernames) == 0) {
        return true;
    }

    // At least one username is not ok here
    $invalidNames = implode(' ,',$notValidUsernames);


$this->setMessage(
        $this->translator->transChoice(
            'form.error.participant_not_found',
            count($notValidUsernames),
            array(
                '%usernames%' => $invalidNames,
                '%last_username%' => end($notValidUsernames)
            )
        )
);

    return false;
}

=============================================== ==========================

Итак, на данный момент

  1. Мы использовали преобразователь для копирования данных из текстовой области и создали массив имен пользователей во время form-> bind ().

  2. Затем мы использовали валидатор, чтобы подтвердить, что каждое имя пользователя действительно существует в базе данных. Если есть что-то, чего нет, мы генерируем сообщение об ошибке, и form-> isValid () завершится ошибкой.

  3. Итак, теперь мы вернулись в контроллер, мы знаем, что у нас есть список допустимых имен пользователей (возможно, через запятую или, возможно, просто массив). Теперь мы хотим добавить их в наш объект потока.

Один из способов - создать службу диспетчера потоков и добавить к ней эту функцию. Так что в контроллере мы могли бы иметь:

$threadManager = $this->get('thread.manager');
$threadManager->addUsersToThread($thread,$users);

Для менеджера потоков мы бы добавили нашего менеджера сущностей. В методе добавления пользователей мы получаем ссылку на каждого из пользователей, проверяем, что у потока еще нет ссылки на этого пользователя, вызываем $ thread-> addUser () и затем очищаем.

Тот факт, что мы включили этот тип функциональности в класс обслуживания, упростит тестирование, поскольку мы также можем создать объект команды и запустить его из командной строки. это также дает нам возможность добавить дополнительные функции, связанные с потоками. Мы могли бы даже подумать о том, чтобы внедрить этот менеджер в валидатор имени пользователя и перенести часть кода isValid в менеджер.

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