PHP разработчик и руководитель небольшого интернет-магазина.
Работаю с Symfony.

Помогаю чем могу новичкам.

•••

Провожу вечерние/выходные занятия в Москве:
Живые уроки по разработке на PHP

Помогу:
• начинающим PHP-программистам стать джуниором и устроиться на работу или закрыть некоторые пробелы
• мануальных тестировщикам освоить автоматизированные браузерные тесты, разобраться с работой веб-проектов изнутри
• верстальщикам и начинающим фронтенд-разработчикам

•••

Пишу в блог:
Абстракции и как они текут
Как джуниор-разработчику найти работу (vc.ru)
Тестовые задания для джуниоров PHP
Контакты
Местоположение
Россия, Москва и Московская обл., Москва

Достижения

Все достижения (56)

Наибольший вклад в теги

Все теги (217)

Лучшие ответы пользователя

Все ответы (764)
  • Какой использовать паттерн проектирования для интеграции c внешним сервисом?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Не складывается все пока в единую структуру

    Глаза боятся, руки делают

    Во внешнем сервисе есть авторизация, добавление/изменение n-го количества сущностей (пользователь, заказ и т.д.). Основная задача паттерна, обойтись малой кровью при замене одного внешнего сервиса на другой, когда потребуется ее заменить. Или возможность переключаться между несколькими внешними системами.

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

    Простой набросок со Стратегией

    Можно придумать некий интерфейс клиента:
    // Принимает ваши учетные данные
    DeliveryClientInterface::__construct(?string $account = null, ?string $password = null)
    
    // Регистрируем покупателя в сервисе
    DeliveryClientInterface::registerCustomer(DeliveryCustomer $customer): int
    
    // Получаем заказы покупателя в сервисе
    DeliveryClientInterface::getOrders(int $customerId): DeliveryOrder
    
    // Добавляем заказ покупателю
    DeliveryClientInterface::addOrder(int $customerId, DeliveryOrder $order): int
    
    // Оповещение покупателя в сервисе о неком действии, связанной с ним в этом сервисе
    DeliveryClientInterface::notifyCustomer(DeliveryEvent $event): bool


    И научить свой проект работать с таким кодом, через интерфейсы
    // что в конструктор сервиса запихнете, например PochtaClient или PickPointClient,
    // с тем и будете работать
    class DeliveryService
    {
        public function __construct(DeliveryClientInterface $deliveryClient, User $user)
    }
    
    $userOrders = $this->deliveryService->getOrders($user->getUuid());


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

    Далее в клиентском коде подключайте нужный вам клиент через конфиг/контейнер и все будет работать.
    Ответ написан
  • Как залогиниться с помощью HTTP-клиента в админку?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Делаю скрипт для проверки битых ссылок в админке
    Должна быть стандартная практика на этот случай

    Тестированию -- тестовые инструменты

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

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

    Примерный вид готового приемочного теста:
    // Тут предварительно логинимся (метод amLogin() не родной, 
         // надо создать его в классе AcceptanceTester)
         public function _before(AcceptanceTester $I)
        {
            $I->amLogin('admin', 'admin');
            $I->amOnPage('/admin');
        }
    
        /** Ниже добавляем через аннотацию провайдер всех ссылок 
         * и данный тест будет выполняться для каждой ссылки в админке
         * @dataProvider linksInAdminPanel
         * 
         *  Или вычесать все ссылки с каждой страницы в методе
         *  и записать в массив и потом по ним идти, проверяя -- была ссылка или нет
         *  
         */
        public function linkIsCorrect(AcceptanceTester $I, \Codeception\Example $pageAdmin)
        {
            $I->amOnPage($pageAdmin['url']);
            $I->seeResponseCodeIs(HttpCode::OK);
        }
    
        // Перечень ссылок в админке
        protected function linksInAdminPanel()
        {
            return [
                ['url' => '/admin/users'],
                ['url' => '/admin/settings'],
                ['url' => '/admin/products'],
                ....
            ];
        }
    Ответ написан
  • Зачем нужен ActiveQuery в Yii2?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Если быть более, конкретным, зачем нужна эта опция, при генерации модели через Gii?

    Чтобы вы могли добавить свои кастомные методы или переопределить родные (all(), one() и другие, короче переопределить родной ActiveQuery и изменить на свои абстракции)

    Вот пример контроллера:
    actual() -- такого метода нет в ORM, а мне нужен, ну вот хочу так пользоваться:
    $trainings = Training::find()->actual($date);

    ActiveQuery

    Ну раз нужен -- добавляем такой метод
    class TrainingQuery extends \yii\db\ActiveQuery
    {
        public function actual($date)
        {
            return
                $this->alias('t')
                ->joinWith(['lesson l' => function($q){
                    $q->joinWith('studio s');
                }])
                ->andWhere(['l.active' => 1])
                ->andWhere(['s.active' => 1])
                ->andWhere(['t.date' => $date])
                ->all();
        }
    }

    Сущность

    В сущности перенацеливаем родной find() на мой кастомный
    class Training extends \yii\db\ActiveRecord
    {
    ...
    public static function find()
        {
            return new \mynamespace\entities\query\TrainingQuery(get_called_class());
        }

    Ответ написан
  • Может ли модель работать с массивом _POST и записывать данные в сессию?

    Maksclub
    @Maksclub
    maksfedorov.ru
    • в модель должны попадать чистые данные (то есть таки да - -в контроллере)
    • вообще с голым _POST не нужно в 2018 году работать, посмотрите это видео: https://vk.cc/8dAijK
    • валидацией может заниматься модель, но в данном случае МОДЕЛЬ ВАЛИДАЦИИ, а не модель сущности,
      модель -- это целый слой, а не конкретный класс или вид классов, это и сущности и сервисы и валидация и т.д....

    POST в моделе
    Не правильно работать с _POST в моделе, тк модель -- по сути синоним вашего бизнеса, бизнес модель, мне кажется странным было бы, если в магазине были бы продавцы, товары, операции дать денег и взять сдачу, данные (купюра, скидка)
    и тут же некий _POST запрос (ну или некая звуковая информация для операций (с битрейтом и частотой:):) мне кажется в бизнес-процесс должно попадать число или купюра, которое обработает некий контроллер (глаз, ухо и продавец в целом как валидатор)

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

    Ну или как пример -- договор между юрлицами, когда вы договорились и заполняете договор, для подписей (по сути сеттер ваш абстрактный) вы УЖЕ ДОЛЖНЫ были проверить контрагента и условия этого самого договора

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

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

    Смотрите как валидация делается в Ларавел:
    public function rules()
        {
            return [
                'name' => 'required|string|max:255',
                'email' => 'required|string|email|max:255|unique:users',
                'password' => 'required|string|min:6|confirmed',
            ];
        }
    Ответ написан
  • Передаю через AJAX клиенту с помощью ф-ии "file_get_content" содержимое php файла (для вывода в popup). В popup пишется также php код. Как исправить?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Кто знает, может, это можно по-короче написать?

    ob_start();
    include(VIEWSPATH . 'pages/login' . '.php');
    return $this->render_ajax(ob_get_clean();


    ob_get_clean() по сути выполняет ob_get_contents() и ob_end_clean().
    php.net/manual/ru/function.ob-get-clean.php
    Ответ написан

Лучшие вопросы пользователя

Все вопросы (40)