Фильтрация данных в Symfony

    Всем любителям Symfony известно что в ней нету компонента фильтр.
    Есть замечательные и удобные Constraints и даже была попытка создать на подобии их фильтры issue на Github, но никто так и не взялся это сделать.


    Когда я в очередной раз столкнулся с задачей фильтрации большого количества данных, полученных от пользователя, я понял что пора решить задачу фильтрации как то более глобально/красиво/удобно.


    Проведя некий research я понял что ничего кардинально нового в фильтрировании на php не появилось. Есть 2 популярных компонента:



    Прошу заметить что последний не совсем хорошо поддерживается автором.
    Для DMS-Filter есть symfony bundle но он не совместим с Symfony 3. Да и код его не понравился, так как я понимал что можно сделать все немного проще.


    Проанализировав ситуацию, я понял что самый быстрый и надежный способ написать bundle для фильтрации который будет использовать хорошо документированные и покрытие тестами фильтры из Zend Filter. Так же хочу добавить что в прошлом я много работал с Zend Framework 1 и мне они были близки и понятны.


    Мне хотелось сделать логику работы фильтров на подобии Symfony Constraints.
    В итоге был написан bundle, что добавляет сервис который может быть использован для фильтрации объекта на основе аннотаций. Кроме того, bundle может фильтровать формы, если он находит аннотированный объект.


    В процессе написания модуля сложностей не возникло. Вот разделы документации которые я использовал во время написания:



    Мне кажется не стоит подробно описывать как пользоваться bundle в данной публикации так как она больше обьясняет зачем я его написал. Да и странно просто копировать информацию с одного места в другое.
    Документацию по использованию bundla и сам bundle вы можете найти на GitHub FilterBundle.


    Хотел бы узнать у сообщества, а как вы решаете задачи по фильтрации в Symfony?

    Share post

    Similar posts

    Comments 25

      0
      Всем любителям Symfony известно что в ней нету компонента фильтр.


      http://symfony.com/doc/current/components/options_resolver.html и filter_var, а так же фильтры твига (для фильтрации при выводе) полностью закрывают все юзкейсы.

      а как вы решаете задачи по фильтрации в Symfony?

      Не фильтрую. Только при выводе. trim-минги формы сами делают, иногда можно в сущностях что-то пофильтровать, но это именно какие-то бизнес-ограничения и деталие реализации сущностей и там этим вещам и место. А так это просто не нужно. Ну или у меня таких задач нет.
        0
        Думаю Вы просто мало с этим стыкались. Пример: как легко и просто можно фильтровать данные которые вы получаете от пользователя в написаном вами API? Или фильтрация данных из entity когда entity нужно передать в serializer/сторонний api?
          0
          Для начала давайте разберемся, какая у меня позиция относительно фильтрации данных в сущностях. Фильтровать данные в сущностях должны сами сущности (то есть я их тех людей, которые против использования сеттеров, анемичной модели и прочей чуши). У меня сущности это полноценные объекты предметной области, а не тупые структурки данных.

          Далее…

          как легко и просто можно фильтровать данные которые вы получаете от пользователя в написаном вами API?


          Из такого рода «фильтрации» мне нужно только trim строк. Предложите мне другие ситуации когда это нужно?
            0
            Конкретный пример из жизни. У вас есть объект который вы получили с сервиса/базы данных которую редактировать у вас нет доступа. В объекте нужно отфильтровать несколько полей перед тем как передать конечному клиенту/сервису.
            Исходя из вашего личного правила что в set/get можно писать фильтрацию и подобные вещи эта проблема решаема. Но куда удобней добавить одну аннотацию к property вместо нескольких строчек кода. Особенно если у вас много таких объектов и они совсем не маленькие. С моим решением как минимум станет легче читать код.
              0
              Вы не поняли. Что именно мне нужно фильтровать? Вот… серьезно, я не могу придумать.

              Исходя из вашего личного правила что в set/get можно писать фильтрацию


              Мое правило — нет никаких get/set в сущностях (только очень редко геттеры и только те что нужно). Есть только объекты предметной области, полноценные сущности с логикой внутри.

              Особенно если у вас много таких объектов и они совсем не маленькие.

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

                >Что именно мне нужно фильтровать? Вот… серьезно, я не могу придумать.
                Например:
                — в varchar хранится int и вам именно int нужно передать дальше.
                — есть текст с переносами \n вам нужно сделать
                — есть html а нужно передать text
                — есть datatime а нужно timestamp
                  0
                  Да но вы же не можете повлиять на нормализацию сторонней базы данных.


                  Я могу мэпить ее на ту структуру объектов с которой мне приятно работать. В прочем у меня нет задач таких где я «не могу на что-то влиять» в плане структуры базы.

                  далее.

                  — каст типов — OptionsResolver
                  — это «фильтрация» при выводе, с этим нет никаких проблем. К сущностям не имеет отношения и к хранению тоже.
                  — есть html а нужно text — так же фильтрация строго при выводе. К сущностям не имеет отношения и к хранению тоже.
                  — есть datetime а нужно timestamp — вопрос фильтрации вывода. К сущностям не имеет отношения и к хранению тоже.
                    0
                    А как можно удобно фильтровать данные при вывод если вы используете Serializer + FOSRest?
                    С учетом того что выводимые данные в таком случае описываются в модели http://jmsyst.com/libs/serializer/master/reference/annotations
                      0
                      Я настрадался с сериалайзерами и просто использую fractal. Да и с symfony serializer я просто свои хэндлеры объектов писал. Код выходит мегаскучный, зато предсказуемый.
                        0
                        Ну и да, для справки. JMS Serializer — старый, не поддерживается уже года два, в нем куча багов и «ускорения разработки» с ним нет никакого. Я его 2 года использовал, мне надоело.
                          0
                          Но есть же проекты которым уже не один год) В таких случаях достаточно сложно перейти на что то другое.
                        0
                        Под выводом подразумевается хелпер твига? Где хранится код трансформации? В самом хелпере или хелпер просто проксирует запросы на другие объекты, передаваемые в хелпер через DI?
                          0
                          Чта?

                          Фильтры твига нужны там где есть твиг. А фильтровать тот же HTML в API не нужно, это сделают на клиенте.
                            0
                            Понятно, echo json_encode(mysql_fetch_assoc($rowset));
                              0
                              Ну между json_encode и mysql_fetch_assoc еще гидрации Doctrine и много других веселых этапов. В частности надо же как-то еще DateTIme сериализовать в формате ATOM и т.д. Но это не фильтрация.
                0
                Мы фильтруем так https://habrahabr.ru/post/281875/
              0
              Очень похоже на Data Transformers. Вам не это надо было?
                +1
                Отвечу за автора: эту задачу можно решить с помощью Data Transformer`ов, «запихнув» фильтрацию в этот слой, но сами трансформеры задачу не решают. Автор же предлагает простой вариант с аннотациями, для быстрой разработки и простого понимания
                  +1
                  Дык можно решить, или трансформеры задачу не решают?

                  Насчет понимания я бы поспорил. Валидация в двух местах теперь, рано или поздно это где-нибудь аукнется. Насчет быстрой разработки, тут спорить не буду, но быстро не всегда значит правильно.
                    0
                    Мою задачу одними Data Transformer`ами не решить. Они работают для форм, но любой объект ими не отфильтруешь. Например есть база данных сторонняя и нету возможности изменять её формат и данные, но получать информацию можно. В моем решении можно отфильтровать любой объект с аннотациями в любой нужный момент.
                      0
                      Я так понимаю вам фильтровать надо, чтобы потом дальше передавать куда-нибудь? Во View, или куда-нибудь в др. место?

                      Расскажите как вы будете это делать если у вас один компонент допускает только br в name сущности, другой компонент допускает только b в name сущности. Сущность одна, приемников несколько, но в пропертях вы можете задать только один фильтр. Ваши действия?

                      Лично мое мнение, подобные преобразования должны происходить ближе к «выводу» и знания, как будет модель преобразовываться точно должны быть не в модели. Например View знает, что что-то надо показывать raw, а что-то с escape.

                      В общем на мой взгляд спорное решение.
                        0
                        > Расскажите как вы будете это делать если у вас один компонент допускает только br в name сущности, другой компонент допускает только b в name сущности. Сущность одна, приемников несколько, но в пропертях вы можете задать только один фильтр. Ваши действия?

                        Использовать группы фильтрации. Ровно так же как это сделано с валидацией.

                        > В общем на мой взгляд спорное решение.
                        Исходя из ваших слов выходит что и Constraint в symfony сделаны не верно.

                        Я долго работал с Zend и после перехода на Symfony самое первое что мне бросилось в глаза это простота/удобство аннотаций. Раньше я все валидации/фильтрации делал в формах и не могу сказать что это удобно.
                          0
                          Constraints это скорее для более удобной работы со схемой. Мне кажется тут больше надо смотреть в сторону Data Transfer Object.
                      0
                      Если задачу решить через трансформеры, то это будет так же «в двух местах», только при этом в двух совсем разных местах. А в случае автора, если он использует аннотации, то «валидация» хоть и будет два раза, но описана она будет в одном месте, что имхо лучше.
                        0
                        В этом и беда, что в одном месте. Например, переносы строк для вывода в теге «p» проходят через «nl2br», а вот при выводе в форме нужно использовать «htmlspecialchars», но никак не «nl2br».

                        Подобную логику нельзя сунуть в модели, можно делать отдельный сервис в стиле декоратора вроде view presenter или data transformer

                Only users with full accounts can post comments. Log in, please.