Блог Максима Федорова

PHP разработчик. Пишу про инструменты в работе, о своих наблюдениях, хобби и проектах.

Портфолио   •   Github    •    Почта    •    Помогаю на Тостере

Как повысить себе зарплату?

Отличный совет от программиста и ИТ-директора Сергея Горностаева (@TheDeadOne) на Тостере о том, как повысить себе зарплату и что нужно учесть:

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

Один из факторов этой бесперебойности — низкая текучка. Сотрудников терять нежелательно. И чем ценнее для компании сотрудник, чем более он профессионален и/или чем больше на него завязано, тем дороже обходится его потеря. Натурально в деньгах. Придётся затратить больше, чем обычно, денег на поддержание работы без него. Придётся затратить деньги и время (те же деньги) на поиск, найм, введение в работу, возможно, обучение нового сотрудника. При этом он может оказаться совсем неподходящих и цикл придётся повторить. Или может оказаться просто хуже прошлого и эффективность отдела снизится. Поэтому, когда сотрудник приходит просить прибавку, руководитель оценивает может ли этот сотрудник уйти или только блефует, насколько легко его будет заменить, какой урон компании будет нанесён его уходом. Потом руководитель оценивает стоимость расширения ФОТ — есть ли резервы, какой сейчас ROI, будет ли больший ROI от реинвестиции этих средств во что-то другое? Если уход сотрудника будет стоить меньше, чем увеличение ФОТа, сотруднику откажут.

Важный момент:

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

Из этого вывод, стратегия проста — увеличивайте собственный профессиональный уровень на столько, чтобы свободно менять компанию, как только вас что-то перестало устраивать.

Роутинг для PSR 7 Response/Request. Часть 2: Проектируем Router

Содержание:

Добавление правил

Придумываем объект маршрутизации и его метод для добавления правила маршрутизации, в который передаем наш путь, HTTP метод и анонимную функцию:

Поиск нужного правила

Чтобы понять, какой обработчик к нашему запросу относится — придумываем метод, в который мы передаем наш Request, а он сам будет производить проверку по правилам выше и возвращать результат, из которого мы сможем извлекать экшн (анонимная функция) и атрибуты — далее мы должны найденные атрибутты в правилах передать в экшн, точнее в объект Request.
По итогу это должно выглядеть так:

Дополнительные методы для добавления правил**

Прежде чем создавать код для объекта Router — думаем над тем, что можно упростить.
Для того, чтобы не указывать HTTP методы в параметрах, мы можем создать методы объекта Router с названием этих HTTP методов:

Это более удобно, не нужно заморачиваться, также будет работать автоподстановка.

Проблема — много методов

В итоге мы придумали кучу методов, и по итогу можно запутаться — какие методы относятся к масиву правил, а что относится к нему.

Для решения этого — роутер разделяем на 2 части: на коллекции роутов и сам роутер.
https://gist.github.com/Maksclub/e09b1f4ad82b53b813ceb8a3043a878b

Помимо упрощения использования — данное разделение позволяет его использовать надежнее, например потом в контроллере, используя объект Router, нельзя будет добавить маршрут, так как у него нет таких методов.

Генерация адресов

Помимо разбора адресов при разработке и поддержке проектов необходимо адреса генерировать.
Конечно в самих представлениях мы можем выводить жестко:

<a href="/blog/<?= $post->id?>">
    <?= htmlspecialchars($post->title)?>
</a>

Но если нам нужно поменять роуты с /blog/ на /post/ или в магазине /product/ на еще какой-нибудь, то придется в ручную менять во всех местах, что может вылезти косяками в работе проекта.

Тогда придумываем метод generate() для нашего объект Router, с помощью которого мы и будем выводить адреса по названию маршрута:

<a href="<?= $router->generate('название_маршрута', ['id' => $post->id]) ?>">
    <?= htmlspecialchars($post->title)?>
</a>

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

Укрощаем регулярное выражение в правилах

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

Тогда вместо:

$router->get('blog_show', '/blog/(?P<id>\d+)', ...);

Можно будет упрощенно использовать так:

$router->get('blog_show', '/blog/{id:\d+}', ...);

Но тогда мы не сможем использовать в данном случае сложные правила, например указать число символов в регулярном выражении, так как фигурные скобки нам усложнят парсинг адресов.

Как мы можем поступить:

  • Сам параметр указать чистым, а регулярное выражение передать отдельным параметром, например:
$router->get('blog_show', '/blog/{id}', ..., ['id' => '\d+']);
  • Сделать систему с необязательными параметрами, а сами параметры передавать как токены и задавать значения по умолчанию:
$router->get('blog_show', '/blog[/{page}]', ..., 
     [
         'tokens' => ['page' => '\d+'], 
         'default' => ['page' => 1],
]);

Будет делать именно второй вариант.

Роутинг для PSR 7 Response/Request. Часть 1: Обзор

Сухая выжимка из видео Дмитрия Елисеева PSR-7 фреймворк 2/6: Контроллеры и маршрутизация.

В ходе этого материала мы создадим полноценный роутинг с добавлением правил маршрутизации, с методами генерации, который будет работать с PSR 7 Request/Response, чтобы рассмотреть основные моменты его устройства, а также рассмотрим в конце популярные компоненты для его замены.

Содержание:

Вводные данные

У нас уже есть входная точка для нашего приложения и подключена библиотека для работы с HTTP PSR 7  — в примерах будет Zend Diactoros.

В итоге наш index.php выглядит так:

По коду:

  1. Подключаем фабрику Zend Diactoros, которая конвертирует глобальные переменные в объекты Request и Response
  2. Создаем объект Response и передаем в него значение GET параметра name или строку ’Guest’
  3. Добавляем свой пользовательский заголовок к объекту Response
  4. Отправляем на вывод, добавляя код ответа и тип контента

Первый роутинг

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

Помимо простых путей, мы можем добавить сюда более гибкие пути, например для блога в виде JSON:

и его отдельных статей, проверяя путь каждой статьи через регулярное выражение:

Это и есть простейший роутинг, но как понимаете — если контент будет сколь заметным или путей будет много — работать с таким кодом будет крайне не удобно.

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

Оптимизация

Чтобы в условиях для каждого роута наши переменные не зассоряли наш эфир и глобальные переменные — поместим этот код в анонимные функции, присвоим их результат переменной $action и после условия выведем результат подходящей условию переменной:

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

Но мы можем воспользоваться методом withAttribute() объекта Request, тем самым изменяя сам объект Request, который мы и передаем в анонимную функцию, в итоге выглядет это так:

ADR (Action Domain Response)

Action Domain Response — частный и упрощенный случай MVC.

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

Простейший вариант:

Но для учета HTTP методов (например для REST API) это будет иметь такой вид:

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

Разработка через TDD

Андрей Солнцев пишет браузерную игру через Unit тесты и UI тесты.
Делает на языке Java, но абсолютно понятно каждому.

Абстракции и как они текут

Статья будет полезна начинающим разработчикам и менеджерам.

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

Абстракция

По определению Википедии, абстракции — это «придание объекту характеристик, которые чётко определяют его концептуальные границы, отличая от всех других объектов».

Вообще абстракции окружают нас повсюду, примеры: город, государство, или коммерческая компания. Операционная система в компьютере — абстракция над драйверами, драйвера — абстракция над самим устройством компьютера. Да и сам компьютер — абстракция, по сути это полупроводники, провода, катушки и прочие железяки, но мы все это концептуально объединили и работаем как с единым целым — значит абстракция.

Каталог в интернет-магазине конечно же тоже абстракция, о нем дальше и поговорим.

Абстракции текут

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

Реальный пример. У нас в компании приходит много товаров из Китая и Турции разных брендов. Исторически сложилось, что в 1С карточки товаров объединяются в группы по брендам (это, к слову, абстракция — менеджер выделил товары по определенным границам, которые совпадают с брендом).

Это хорошо работает — менеджеру удобно работать с клиентом и показывать целые линейки брендов. Но возникает как всегда НО — часто приходит товар неизвестного бренда в количестве 1-2 вещей и таких товаров много. Проблема — под одну вещь заводить группу с брендом — неудобный и лишний труд. Тогда менеджер решает создать помимо группы брендов одну отдельную группу — группу «Китай», в которую накидывает единичные товары произвольных брендов.

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

Проблемы утечек

Проблема на первый взгляд невинная — менеджеру удобно работать, в случае чего новый менеджер разберется, хотя если товаровы больше десятка тысяч — поиск какого-то товара по бренду (когда сам товар в группе «Китай») может попить ему крови.

Но 1С в 21 веке почти всегда работает бок о бок с веб-системами — выгрузка в интернет-магазин, выгрузка партнерам, отчеты и статистика.

Рассмотрим интернет-магазин. В интернет-магазине есть куча абстракций и самые явные — меню категорий и фильтр товаров. Исторически сложилось, что категории в первую очередь строятся по виду товара (джинсы, куртки) или полу (М и Ж), но не по брендам. Тогда бренд нам нужно сделать одним из свойств фильтра и тут мы получаем первые проблемы — пользователь сайта среди возможных брендов видит бренд с названием «Китай», так как мы взяли утекшую абстракцию за значение свойства.


Избегайте утечек абстракций по возможности — это чревато проблемами.



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

Текут в любом месте

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



Каково было мое удивление, когда спустя некоторое время в меню для сайта я увидел данные для  продавцов розничного магазина :) Да-да, это чистой воды утечка абстракции.

Чем это чревато? Меню может быть несколько для нескольких сайтов, к какому из них прикреплять данные для розничного магазина? :) Или будет путаница в отчетах, ведь в некоторых меню нет этих значений.

Домены .dev в Хроме принудительно переадресуются на https (это нормально)

Google Chrome насильно перенаправляет все домены вида example.dev начиная с версии браузера 63 от 7 декабря 2017, даже если их используете локально и они прописаны в файле hosts.
Не помогает даже настройка браузера в HSTS.
Потому у некоторых программистов могут вылезти баги... сегодня вот не мог понять, что за Х**** приключилась.

Дело в том, что этот домен принадлежит Гуглу и они решили насильно всех пользователей своего браузера кидать на https. Это также скорее всего будет и на производных Хрома — например в Яндекс-браузере.

Что делать в этом случае?

Пара вариантов на самом деле:

  • Свои локальные проекты прописывайте с доменами .localhost, .invalid, .test, or .example и др неконфликтные синонимы
  • Используйте Firefox (или Safari) для разработки
2017   chrome

Почему массив начинается с нуля?

UPD: читаем комментарии — мое объяснение не совсем верное, но будет понятно каждому.

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

Попробую наглядно объяснить, почему так принято в этом самом программировании и зачем это нужно.

Точка отсчета

Чтобы обозначить проблему при использовании отсчета массива не с нуля, а с единицы, приведу пример:

Вам нужно вывести набор значений — например динамику посещений кафе и эффективность той или иной рекламы... нужно вывести количество людей за месяц.

Когда вы будете выводить на графике или в таблице данные, вы сразу столкнетесь с тем, что 1 — будет результат за январь, а 12 — результат за декабрь. А где же будет исходно значение? Относительно чего будет прирост в январе?

Правильно! К моменту подсчета у вас должно быть какое-то значение, оно может быть равно нулю или иметь какое-то численное значение. И в массиве значений оно будет первым:

{
0 => 2 тыс // столько было например в прошлом месяце, если кафе открылось, то оно будет равно 0
1 => 1.7 тыс // посещения в январе
2 => 2.1 тыс // посещения в феврале
3 => 1.9 тыс // посещения в марте
...
12 => n тыс  // посещения в декабре
}

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

Менять этого уже точно никто не будет, всем новичкам нужно понять и попробовать прорешать несколько задач с массивами и стеками, чтобы разобраться что к чему.

Когда нужно вывести первый элемент массива уже на автомате пишешь

$array[0]

где 0 — индекс первого элемента :)




Вроде как даже создатель языка Python (по-нашему Питон) хотел ввести индексацию массивов с единицы, но сообщество восприняло это в штыки...

Поиск товаров в интернет-магазине от Яндекса

У Яндекса в бета-тестировании появился крутой сервис, который помогает искать товары в интернет-магазине.

Это именно полнотекстовый поиск по характеристикам, названиям, брендам и описанию товаров. Чтобы он заработал — нужно добавить свой магазин в поиск и добавить YML-фид, который подходит для Яндекс.Маркета.

Пример формата подходящего файла




YML-фид для поиска

Это основной момент в работе поиска. Для индексации Яндексу нужны данные в формате Яндекс.Маркета. В этом файле нужно указать все актуальные данные для каждого товара. Чем подробней будет расписан товар, тем точнее будет искать поиск — указать пол, цвет, мощность и при наборе этих значений поиск будет выдавать максимально подходящие варианты.

Пример работы с параметром «Цвет»




Лингвистическая мощь Яндекса

Обычный поиск на обычном сайте имеет недостаток — он ищет точное совпадение символов. С поиском Яндекса можно позволить себе писать с ошибками или забыть поменять раскладку.

Вводим: «l;bycs gfynfvj xthy», по-русски это «джинсы пантамо черн»

Отчасти такое могут делать поисковые решения Sphinx или ElasticSearch, но для магазина без штата программистов — это решение лучшее.




Койот — это шакал

Этот забавный кейс был описан в статье основателя «Мосигры». При проектировании своего поиска, команда интернет-магазина анализировала запросы пользователей и добавляла к играм ассоциации, чтобы можно было находить максимально широко нужную игру. Так они обнаружили, что их популярную игру «Шакал» искали по запросу койот.

В чем фишка. Можно воспользоваться этим лайфхаком и  добавить у товара в своем магазине поле «Синонимы» и в него складывать через запятую все синонимы к товару, или это можно делать программно или через Эксель, как угодно... и останется только вывести в YML-файл и поиск должен подхватить эти значения.




Далее будут статьи:

  • Вывод поиска через виджет от самого Яндекса
  • Вывод результатов поиска через Яндекс.АПИ в формате JSON (как на скринах)



Если хотите себе такой — пишите на hello@maksfedorov.ru

Фильтрация задач по спискам или доскам в Trello

При работе с Trello иногда появляется задача — вывести сразу с нескольких досок актуальные задачи. или вывести по исполнителю и активности. Например есть 3-4 доски с разными проектами (разные сайты, или по отделам — дизайн и разработка) и нужно например вывести со всех досок одновременно все задачи из списка «Задачи» или из списка «ToDo».

Оказывается есть команды, которыми можно выводить тикеты как в фильтре — по названию списков, по активности, по людям и особенностям (с вложениями или описанием).

Например вывожу тикеты с разных досок, в каждой из которых есть список Задачи:

Доступные операторы

-operator — если добавить минус, то задачи с последующей отборкой не выведутся, например -has:members выведет все тикеты, никому не назначенные
@name — выведет тикеты, назначенные данному пользователю, @me выведет ваши картчоки
label: — выведет тикеты по точному названию, например label:«fix header»
board:id — выведет тикеты с определенной доски
list:name — выведет тикеты по названию списка
has:attachments, has:description, has:cover, has:members — выведут тикеты, которые содержат вложения, описания, обложку или участников соответственно
created:day — выведет тикеты, созданные за сутки, сожно вывести за неделю (week) или месяц (month)
edited:day — выведет тикеты, отредактированные за сутки
is:open, is: archived — выведут открытые и закрытые тикеты


Можно перейти отдельно на страницу поиска и фильтровать свои тикеты:

2017   trello   Советы
Ctrl + ↓ Ранее