• Дайте оценку верстке?

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

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

    Из наиболее заметного - заглавная картинка с автомобилем в PNG которая занимает почти 600кб и из-за этого грузится весьма и весьма неспешно (и заметно для пользователя). В целом это по большей части косяк дизайнера, не приложившего усилий к тому чтобы выбрать правильную графику (автомобиль снят явно на улице и отражения в стёклах дают существенный вклад в визуальный шум и, как следствие, в размер картинки, нужно было выбирать фотографию сделанную в специальном помещении). Кроме того дизайнер, очевидно, не слышал про требования к такси в Нью-Йорке и рисовал как взбредёт в голову, но оставим это на его совести. Сочетание фоновой картинки, на которой весь траффик едет в обратном направлении и делает автомобиль такси нарушающим правила дорожного движения - тоже на совести дизайнера.

    Однако и в этом случае и, тем более, в случае фоновых изображений ниже по странице вы допускаете ошибки с выбором форматов файлов, способами их вставки в страницу и оптимизацией. К примеру из картинки с автомобилем можно выжать почти 100кб просто за счёт использования оптимизаторов. Гораздо грустнее ситуация с фоновыми картинками ниже по тексту. Во-первых вы сохраняете фотографии в PNG, получая на выходе файлы по мегабайту, хотя они же в JPEG занимали бы в 5-10 раз быстрее. Во-вторых вы, скорее всего, сохранили фоновые картинки уже обработанными (затемнёнными). Я не видел макета, но предположу что там эти картинки стоят в их оригинальном виде и на них наложены какие-нибудь фильтры. На первый взгляд кажется что проблемы нет, но на практике (в случае вёрстки для реального сайта) вы вынуждаете человека который будет поддерживать сайт либо готовить картинки с такой же пост-обработкой либо мириться с тем что стиль сайта меняется. Правильное решение здесь - грузить картинки как они есть и реализовывать фильтры на CSS, тем более что здесь это делается элементарно через multi background или псевдо-класс с полупрозрачным фоном. Очевидно также что для таких тёмных картинок вполне можно использовать JPEG с меньшим качеством и тем самым существенно сэкономить пользователям трафик.

    Ещё одна проблема связанная с фоновыми картинками - вы не подкладываете под них близкий по цвету solid color. Попробуйте включить в dev.tools "Network throttling", отключить кэш и перегрузить свою страницу - думаю вы поймёте что я имею в виду - белые блоки с белым текстом стоят довольно продолжительное время, постепенно заполняясь довольно тёмными картинками. Если бы background-color под ними был бы чёрным - проблемы бы не было.

    Далее - логотип. Обычно логотипы разрабатываются отдельно и даже если он выглядит просто набранным шрифтом - это вовсе не значит что это не так. Логотип Google, Microsoft или Яндекс - тоже просто название, но, надеюсь вы не сверстаете их, написав надпись текстом? В общем логотип = картинка, лучше в векторе. Сейчас даже одно съезжание слогана на пиксель влево относительно названия уже рушит всю конструкцию логотипа.

    Обратите внимание на то как вы работаете с формами. Все поля в форме являются <input type="text">, хотя часть названий явно намекает на date / time селекторы, а "Choose Vehicle" - на список выбора.

    Хотелось бы отметить работу с иконками - их всё-таки лучше хранить в SVG и либо требовать с дизайнера либо подбирать на том же Icon Finder. При этом оформление (те пресловутые жёлтые кружки) лучше делать через CSS т.к. это позволяет вам существенно гибче работать с размерами элементов.

    Есть всякие недочёты касающиеся responsive, к примеру, внимание как отображается блок "Our Tariffs" в размере чуть более 600px, в частности название тарифа и описание.

    Пожалуйста обратите внимание на то что вы используете два разных меню для desktop и mobile представления. В целом в вашем случае меню довольно простое и можно было бы обойтись одним. Конечно две копии используют часто, но у этого решения есть свои недостатки (в частности отсутствие синхронизации состояния), так что вы должны осознанно принимать решение по таким вопросам. Кроме того inline обработчики onclick там явно могут быть заменены на элементарный
    document.querySelectorAll('.menu a, .menu-hover a').addEventListener()
    что явно сделает код более простым и поддерживаемым.

    Ещё один важный момент который зачастую опускают при вёрстке - поведение макета с реальными данными. То что дизайнер в макете понапихал везде lorem ipsum и тексты примерно одинакового размера - отнюдь не означает что на реальном сайте эти условия будут соблюдаться. Отсутствие навыка проверять поведение макета в изменяющихся условиях ведёт к множеству ошибок которые не видны в условиях синтетических данных. К примеру попробуйте в блоке "We Do Best Than You Wish" (претензии по поводу английского языка оставим в стороне) в любом из элементов банально увеличить количество текста в 2-3 раза. В Chrome это приводит только к излому сетки, в Firefox - ещё и к изменению размера иконки. При этом я предполагаю что Firefox ведёт себя правильно т.к. пропорции элементов изменились, а ограничения размеров на картинки у вас не заданы.

    В целом похоже что макет верстался и проверялся только в Chrome. К примеру посмотрите как ведёт себя картинка с рукой и телефоном в Firefox при изменении размеров. Опять же Firefox вполне корректен т.к. вы не обрезали картинку корректно, предпочитая выгрузить "как есть" и подгонять положение в CSS, но забыв при этом про overflow: hidden для контейнера.

    Теперь перейдём к CSS:

    Обратите внимание на то как вы подключаете внешний шрифт:
    family=Lato:400,700,700i,900,900i&amp;subset=latin-ext
    . Возникают два вопроса:
    1. Зачем вам subset=latin-ext на сайте где есть только базовая латиница?
    2. Как вы выбирали начертания? У вас подключаются 5 начертаний (400, 700, 900 + два italic'а), при этом grep по CSS даёт значения font-weight 200, 300, 400, 500, 600, 800 и ни одного italic. Вам не кажется что эти множества почти не пересекаются?


    Кроме того вы постоянно забываете про fallback шрифты что на медленном интернете и при отсутствии инструкций для font loading приводит к невидимому контенту страницы на период загрузки.

    Отсутсвие ограничения по ширине для .wrapper приведёт к недопустимо широкому сайту на больших мониторах с высоким разрешением. Можете уменьшить масштаб страницы до 50% и полюбоваться результатом.

    В стилях повсеместно используются достаточно общие названия классов в global namespace. К примеру кто бы мог подумать что стилизует селектор .text? Вы уверены что нигде больше на сайте подобный селектор не встретится? Даже при дальнейшем развитии сайта? Другими словами именование селекторов - важная часть работы, вы можете использовать любую методологию (тот же БЭМ или что-то ещё) или разработать свою, но ваш код не должен ломаться при добавлении ещё пары блоков, особенно если это будет делать другой человек.

    Списки элементов, к примеру тот же .product-cont лучше делать именно списками, а не распихивать элементы по столбцам вручную, благо flexbox и column layout здесь всё прекрасно сделают за вас, зато имея одноранговый список вы обеспечите себе куда большую свободу действий.

    Использование id в качестве CSS селектора - плохая практика, но у вас таких мест немало, 11 штук.

    Уверен что мог бы найти ещё что-то, но надеюсь для затравки хватит, и так много получилось... :)
    Ответ написан
    4 комментария
  • Не могу подключить иконочные шрифты?

    @Flying
    .ai - это формат Adobe Illustrator, он явно не является шрифтом. Просто используйте реальные веб-шрифты (расширение должно быть .woff2 или .woff).

    Если у вас есть только desktop версия шрифта (к примеру с расширением .ttf или .otf) - то веб-версию можно получить используя FontSquirrel generator.
    Ответ написан
  • Контроль доступа в symfony, не совсем понимаю тебя?

    @Flying
    Symfony - это в первую очередь framework для обработки HTTP запросов и поэтому многие компоненты по-умолчанию "заточены" именно под этот сценарий.

    В конечном итоге вся Symfony Security построена вокруг понятия "токена" (TokenInterface) и большинство частей Security компонента достаточно абстрактны для того чтобы из них можно было собрать весьма разнообразные схемы разделения прав доступа.

    Вам необходимо определить для себя что означает "привязка привилегии к функционалу" с точки зрения кода приложения. Вы планируете оставить это разделение прав доступа на уровне "пускать / не пускать по определённому route" или вы будете в сервисах приложения вызывать что-то подобное Security::isAllowed()? Symfony по-умолчанию реализует первый подход, но ничто не мешает вам организовать собственную реализацию основных компонентов. Если вам нужен ACL (а именно так называется "привязка привилегии к функционалу") - то эта функциональность вынесена в отдельный ACL bundle в Symfony 4, скорее всего именно она вам и нужна.

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

    Если говорить о работе Symfony Security в контексте обработки HTTP запроса - то аутентификация производится в процессе прохождения запроса через т.н. firewalls, их задача - каким-либо образом определить что это за пользователь и сформировать token. Процесс авторизации построен вокруг т.н. voters которые "голосуют" за/против/воздержался по вопросу "пускать/не пускать", в документации есть целый ряд примеров. Надеюсь факт того что voters могут быть произвольными и иметь любую собственную логику - довольно очевиден. К примеру тот же ACL bundle как раз реализует voter который принимает решение о доступе на основе ACL list'а (который, конечно же, может находиться и в базе данных), но в целом это частный случай.
    Ответ написан
    1 комментарий
  • Как побороть, что fputcsv добавляет словам с пробелами двойные кавычки?

    @Flying
    Это связано с форматом CSV как таковым :) А также со значением аргумента $enclosure который вы, очевидно, оставили по-умолчанию. Всё это есть в документации.
    Ответ написан
  • Скорость работы MySQL или Файлы?

    @Flying
    Выбор сильно зависит от того что вы собираетесь делать с этими данными. Если вам нужно (и, главное, будет нужно и в дальнейшем) только читать / писать строго определённые одиночные записи - то можно использовать и файловую систему, хотя хранить все файлы в одной папке - очень плохая идея, нужен sharding чтобы оптимизировать работу (вспоминаем как работает поиск по файлам в каталоге). Кроме того нужно помнить что открытие файла - довольно дорогая операция.

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

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

    В целом ваш запрос сводится к "использовать ли мне программу, специально предназначенную для нужной мне функции или написать свой велосипед". Уверен что в подобной формулировке вам легче будет самому ответить на свой вопрос.
    Ответ написан
    Комментировать
  • Чем заменить PUG?

    @Flying
    Посмотрите в сторону Timber, он интегрирует Twig в Wordpress, работает весьма хорошо.
    Ответ написан
    Комментировать
  • Мобильная верстка — почему уменьшается масштаб сайта?

    @Flying
    Проверьте наличие в коде сайта настроек viewport'а, что-то вроде этого должно помочь:
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    Ответ написан
    1 комментарий
  • Как правильно верстать сайт с арабскими и хинди языком?

    @Flying
    Поскольку недавно пришлось добавлять в одном проекте арабский язык - поделюсь полученным опытом:

    Вёрстку как правило переделывать не нужно, обычно достаточно изменения стилей, но только в случае если сам сайт свёрстан с применением современных техник, в первую очередь flexbox. Если у вас там float'ы или таблицы - то ой, задача по сложности вырастет на пару порядков и почти наверняка не обойдётся без доработки html кода. Если же в этом аспекте всё в порядке и ваш проект построен на flexbox - то основная масса изменений сводится к одной строчке CSS:
    body {
        direction: rtl;
    }

    это "перевернёт" все горизонтальные flexbox'ы и по сути сделает за вас всю основную массу работы по адаптированию сайта к RTL языкам. Конечно, в зависимости от того как именно у вас будет подключаться стиль (отдельные стили для RTL или только патч или всё вместе) реальный селектор может быть, к примеру html[dir=rtl] + body, но это уже детали.

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

    1. Горизонтальные отступы. Это самая большая часть работы по адаптации т.к. вам необходимо будет "перевернуть" и их тоже, заменив, к примеру, margin-left на margin-right и наоборот, то же самое для padding'а
    2. Абсолютное / относительное позиционирование в горизонтальной плоскости. Речь идёт о свойствах left и right, их, как можно догадаться, тоже необходимо поменять местами
    3. Размеры шрифтов. Поскольку, к примеру, арабский шрифт, обычно выглядит меньше и тоньше чем, к примеру, английский - возможно возникнет потребность увеличить размеры шрифтов (font-size, line-height) и, возможно, подстроить стилизацию (font-weight)
    4. text-align - в ряде ситуаций может потребоваться изменить его на противоположный
    5. :first-child и :last-child, стоит быть внимательным и перепроверить корректность получаемого результата, к примеру если к этим псевдо-элементам добавляется дополнительный отступ - вам, возможно, придётся менять местами и селекторы
    6. Нужно адаптировать визуальные элементы содержащие направление, к примеру стрелки / уголки и т.п. В ряде случаев их можно повернуть, но где-то необходимо будет рисовать отдельную версию


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

    Общая схема адаптации которую я использовал в проекте:
    1. Добавление direction: rtl
    2. "Переворачивание" отступов, это самая большая часть работы т.к. они чаще всего встречаются
    3. Проверка вёрстки, для каждого выпавшего элемента добавление патчей с использованием mixin'ов, ссылку на которые я дал выше
    4. Подбор изменений для шрифтов, адаптация шрифтовых параметров


    Пример выдернутого наудачу из проекта куска для демонстрации патчей, это стиль добавления иконки к строке текста, для RTL языка её нужно было опускать ниже:
    &.with-icon {
        $icon-size: 1.85em;
        @include offset(h $icon-size 0);
    
        &:before {
            // Позиция иконки меняется на противоположную
            @include hpos($left: -1em, $auto: true);
            font-size: $icon-size;
            // Подстраивается высота иконки относительно текста
            @include ltr() {
                top: 45%;
            }
            @include rtl() {
                top: 65%;
                // Стоит обратить внимание что для RTL языков иконка дополнительно переворачивается, 
                // там стрелка, так что работает нормально, но в других местах это может быть по-другому
                transform: translateY(-50%) rotate(180deg);     
            }
        }
    }


    Надеюсь это описание будет полезным :)
    Ответ написан
    2 комментария
  • Аналог команды cp -r в Windows?

    @Flying
    В принципе есть порты множества Unix утилит на Windows, в частности в рамках проекта GnuWin32. Конкретно cp находится в CoreUtils. Я пользуюсь уже много лет - во многом обеспечивает близкое к Unix окружение, с поправкой на ограничения cmd.exe конечно, зато нативно, без CygWin.
    Ответ написан
    Комментировать
  • Как правильнее реализовать код обработки AJAX запроса от разных сущьностей к одному контроллеру?

    @Flying
    В зависимости от реальных потребностей вашего приложения можно посмотреть в сторону использования наследования entities. К примеру возможно ваши "category, faq, article" являются частными случаями общей логической сущности Content, в этом случае можно было бы запрашивать репозиторий именно для базовой entity и дальше у вас не будет разделения логики работы с теми же тегами.

    Кроме того ничто не мешает вам вынести код работы с тегами в отдельный сервисный класс (назовём его TagSearchService), сделать для каждого типа "category, faq, article" отдельные actions в их контроллерах которые будут обращаться к сервисному классу с разными (и в этом случае заранее известными) параметрами. Т.е., проще говоря, в каком-нибудь CategoryController::search() вы могли бы вызвать TagSearchService и передавать ему Category::class вместо того чтобы полагаться на приходящие извне данные. Если переходить на такую схему - то разные классы для элементов форм (CategoryType и FaqType в вашем примере) тоже естественным образом заменяются на один класс (какой-нибудь TagSearchType) т.к. между ними не остаётся различий. Кроме того не нужно будет передавать извне имя класса - по-моему это плохая идея в любом случае.

    Если развивать эту идею дальше - то логично вырисовывается интерфейс TaggableInterface для entities которые могут иметь теги. Это естественным образом приводит к возможности в compiler pass собрать список таких entities и передать их в TagSearchService. Возможно потребуется для каких-то целей :)

    Далее, по поводу фильтрации тегов. Всё-таки при построении запросов мы ведём речь о DQL, так что есть ненулевой шанс (хотя доказать это я и не могу, надо пробовать) что вытаскивать id из тегов не нужно, достаточно передать массив самих entities в notIn(). Если же это и не так - код можно переписать с использованием array_map(), возможно это сделает его понятнее. Также findOneBy(['id' => $entityId]) очевидным образом меняется на EntityManager::find() что несколько проще выглядит.

    Насчёт идеи вытягивать именно id тегов - вряд ли в этом есть особый смысл если только у вас не очень нагруженное приложение.
    Ответ написан
  • Есть ли хорошие курсы по Symfony?

    @Flying
    Есть официальная учебная платфома у SensioLabs, правда недешёвая.
    Ответ написан
    Комментировать
  • Почему regEx не работает в firefox?

    @Flying
    regex101 тоже не может разобрать вашу регулярку, так что видимо вопрос в синтаксисе. Да и парсить адрес регуляркой - так себе идея, что вы будете делать если последовательность элементов в адресе будет другой или если там будут опечатки?

    Возможно вам стоит рассмотреть использование внешних сервисов которые нормализуют адреса? К примеру я в одном проекте использовал DaData и они действительно хорошо работают (не реклама :) ).
    Ответ написан
    4 комментария
  • Как сделать такую фигуру с помощью css?

    @Flying
    Для реализации на CSS потребуются как минимум conic-gradient() и CSS Masks, и то и другое весьма далеко до широкой поддержки браузерами, поэтому гораздо проще будет просто сделать SVG картинку.
    Ответ написан
    2 комментария
  • Как сделать связь одного поля таблицы с несколькими сущьностями (entity)?

    @Flying
    Вы уже используете DiscriminatorMap который является правильным решением в данной задаче. Вам ничто не мешает теперь иметь в наследуемых классах отдельные свойства содержащие связи с конкретными entities, специфичными для каждого конкретного типа пункта меню. Вы также вполне можете объявить getEntity() / setEntity() в классе MenuItem абстрактными, а в дочерних классах реализовывать их. Конечно типы на уровне языка вам будет не задать, но никто не будет вам мешать использовать PHPDoc для создания type hints.
    Ответ написан
    1 комментарий
  • Почему Firefox не всегда предагает сохранить пароль?

    @Flying
    Сохранение пароля не предлагается если сайт использует нестандартную работу с формами. К примеру если вместо сабмита формы происходит отсылка содержимого через AJAX запрос, если сами элементы формы находятся не в теге <form>, если вместо кнопки submit'а используется что-то другое (например ссылка с JavaScript обработчиком) и т.п. Кроме того сохранение формы не предлагается если сайт напрямую это запрещает, добавляя атрибут autocomplete="false" в элементы форм.

    Я как правило в этих случаях открываю dev tools, удаляю всю нестандартную обработку, добавляю обычную кнопку, удаляю ненужные атрибуты, заполняю логин / пароль, жму submit, сохраняю логин / пароль и перегружаю страницу. В 95% случаев помогает, единственный нюанс появляется в случае запрета на autocomplete - в этом случае при клике на поле логина Firefox предложит выбрать сохранённый логин и при выборе заполнит пароль.
    Ответ написан
    1 комментарий
  • Для чего предназначена данная примесь?

    @Flying
    Этот код проверяет что элементы в $map расставлены в порядке возрастания значений
    Ответ написан
    Комментировать
  • Слои приложения в Symfony?

    @Flying
    То что описано в пункте про "атомарные операции" больше всего похоже на методы в entity repository в Doctrine.
    • Если эти операции специфичны для каких-то конкретных entity - можно реализовывать прямо в методах соответствующих репозиториев
    • Если они общие для всех entities - то проще всего вынести их в промежуточный абстрактный repository и наследовать остальные custom repositories от него
    • Если они общие для части entities - можно, к примеру, определить дополнительные интерфейсы, показывающие какие операции применимы к каким entities (у меня, например, зачастую используется ActivitableInterface для entities для которых есть флаг isActive) и дальше либо реализовать промежуточный абстрактный класс репозитория и наследовать custom repositories от него либо вынести эти операции в traits и подключать по мере необходимости.



    Про "команды" не очень понятно, это про класс Command из symfony/command? Если да - то там есть раздельная инициализация через initialize(), туда передаётся OutputInterface и если сильно нужно - можно там вызвать некий отдельный сервис. Если не хочется прокидывать сервис вручную в каждую команду - можно автоматизировать это через setter + compiler pass либо через аннотицию @required
    Ответ написан
    2 комментария
  • Какие сетки вы используете в своих проектах? И куда двигаться дальше?

    @Flying
    Использую Susy (пока что 2-ю версию в силу ряда причин, но вообще рекомендую 3-ю). Причина выбора - возможность строить произвольные сетки (в том числе нерегулярные) при полном отсуствии навязывания каких-либо технологий реализации сеток. Мне в этой библиотеке нравится именно её гибкость т.к. она даёт только математику сетки. Одновременно с этим для новичка подобный подход может оказаться слишком сложным, но познакомиться с ним imho стоит. На самом деле сделать базовую сетку самостоятельно - очень несложно, особенно если использовать препроцессоры, имеет смысл разок попробовать ее реализовать.

    Один из моментов который не очень хорошо решается "стандартными" сетками - отсутствие поддержки внешних отступов внутри контейнера когда сетка должна занимать не всю ширину, а (ширину минус отступы). Конечно это решаемое ограничение, но оно имеет место быть. Подход bootstrap с заданием сетки через CSS классы лично мне не нравится т.к. порождает кучу лишних CSS классов которые в случае смены layout'а необходимо менять в коде (а не в стилях) проекта. Но здесь опять же кому что, да и у подхода с CSS классами есть свои плюсы - можно дать возможность бОльшего контроля layout'а разработчикам.

    Также стоит отметить что "правильных" сторон нет, есть множество подходов с которыми стоит ознакомиться и из которых нужно уметь выбрать тот который лучше подходит для решения задачи.
    Ответ написан
    Комментировать
  • Symfony 4 - Best Practices for Reusable Bundles в чём смысл, и как всё таки создать bundle?

    @Flying
    Очевидно вы имеете в виду вот эту статью в документации Symfony.

    Если прочитать текст более внимательно - то там не сказано что bundles теперь запрещены или не приветствуются. Речь идёт о том что bundles стоит использовать в том случае если вы хотите оформить часть своего кода в отдельный package который можно использовать и в других проектах. Основное же приложение теперь не стоит оформлять в виде bundle, а можно писать напрямую. Предлагается не складывать в /src кучу разных bundles, а иметь один общий код приложения, зависимый только от внешних packages (пусть даже написанных вами) которые устанавливаются через Composer.

    Т.е. к примеру структура кода подобная Sylius теперь не считается хорошей практикой.

    Думаю что одна из причин такого изменения - переход на Symfony Flex и автоматизированную поддержку регистрации / конфигурации bundles через recipes.
    Ответ написан
    Комментировать