Задать вопрос
  • Можно ли в php при присвоении переменной значения - функции, в качестве аргумента этой функции отправить эту самую переменную? Как это написать?

    onqu
    @onqu
    weasy
    ($validation = Validation::factory($_POST))
        ->rule('user', 'CustomValidation::is_login', array(':value', $validation));
    Ответ написан
    Комментировать
  • Как правильно построить индекс для mongodb?

    onqu
    @onqu
    weasy
    При использовании в выборке sparse index на "неоднородных" документах нужно использовать hint, плюс не нужно добавлять $exists. И конечно же, не нужно забывать, что индекс с 2мя значениями (1:0 / true:false и тд) всегда малоэффективен.

    Документы неоднородны, когда отсутствуют какие-то свойства, которые есть у других документов, и по этим свойствам строится индекс.

    db.documents.createIndex( { is_processed: 1 } , { sparse: true } )


    db.documents.find({is_processed: 1}).hint({is_processed: 1}).limit(200)

    или
    db.documents.find().sort({is_processed: 1}).hint({is_processed: 1}).limit(200)
    Ответ написан
    2 комментария
  • Каким должен быть правильный контроллер?

    onqu
    @onqu
    weasy
    Никаким. Его не должно быть. Вообще. Только сразу камнями не закидываете.

    Разъясню.

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

    2. Реализации
    В небольших приложениях в контроллеры наваливают бизнес логику, чтобы не тратить время на построение архитектуры. Оно себя оправдывает. Да мы лишаем себя возможности модульных тестов, но функциональные остаются рабочими. И это является противоречием назначению контроллера.
    class RobotsControlller
    {
        public function inactiveRobots()
        {
            $robots = Robots::find()->where(['active' => false])->all();
            foreach ($robots as $robot) {
                $robot->active = true;
                $robot->save();
            }
    
            return $this->render('robots', [
                'robots' => $robots
            ]);
        }
    }


    В проектах средней величины уже пытаются выносить бизнес логику в сервисы. Сервисный слой это и есть модель из множества классов. В контроллере вызывается один-два из этих сервисов и данные передаются в представление. И все бы хорошо, связующее звено для сервисов..., но это грамотно завуалированная бизнес логика, которую отчетливо видно лишь в крупных проектах.
    class RobotsControlller
    {
        public function inactiveRobots()
        {
            $container = $this->getContainer();
            $robotsService = $container->getRobotsService();
            $robots = $robotsService->getInactiveRobots();
    
            $stationService = $container->getStationService();
            $stationService->acivateRobots($robots);
    
            return $this->render('robots', [
                'robots' => $robots
            ]);
        }
    }


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

    3. Как же все-таки правильно.
    Отказываться от контроллеров и пилить свой велосипед на текущий момент слишком затратно по времени на всех проектах меньше больших. Мой ответ будет прост - использовать здравый смысл, сохранять семантику и не забывать об удобстве. Называете вещи своими именами и придерживайтесь однородности. Если строите api на типах resource (get | delete | put | post) то придерживайтесь этого стиля, не мешайте со стандартными экшенами. Разобравшись в одном будет проще понять работу всего остального.

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

    В каждом MVC много других маленьких MVC, скажете вы, ища оправдания необходимости контроллера.

    p.s. можно начинать кидать камни)
    Ответ написан
    3 комментария
  • Почему не работает функция loginUsingId в Laravel?

    onqu
    @onqu
    weasy
    Похоже, что класс пользователя не реализует контракт \Illuminate\Contracts\Auth\Authenticatable
    Ответ написан
    Комментировать
  • Веб-сокет у некоторых клиентов работает только через VPN. Почему так?

    onqu
    @onqu
    weasy
    Есть злые провайдеры, которые режут http заголовки, например Upgrade: websocket. Попробуйте начать с этого.
    Ответ написан
    1 комментарий
  • Откуда у меня деньги на аккаунте unrealengine и как вывести?

    onqu
    @onqu
    weasy
    Этому событию уже больше года. Когда Эпики отказались от платной подписки, всем кто хоть раз оплачивал подписку зачислили на счет по 30$. Вывести, скорее всего, нельзя. Свои потратил в маркете.
    Ответ написан
    Комментировать
  • В чем суть роутера на php?

    onqu
    @onqu
    weasy
    1. Здесь пугают всякими контроллерами, ларавелями. Давайте жить проще. Для начала дадим определение модному слову роутер. Это маршрутизатор. Что делает маршрутизатор? Правильно. Обрабатывает маршруты, являясь связующим звеном. Маршрутом для web сайта принято считать метод запроса [GET, POST, PUT и другие] и компоненты URI.

    например: https://ru.wikipedia.org/wiki/URI?foo=bar#title
    [схема: https] :// [источник: ru.wikipedia.org] [путь: /wiki/URI] [запрос: ?foo=bar] [фрагмент: #title]


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

    Сама работа, как правило проста: от клиента приходит запрос, маршрутизатор перебирает все заданные ему пути до первого совпадения. При совпадении вызывается определенная вами функция, которая возвращает ответ клиенту.

    2. Он необходим, если в приложении одна точка входа, когда любой запрос приходит на один файл.

    3. Простой пример
    // файл index.php
    
    // Маршруты
    // [маршрут => функция которая будет вызвана]
    $routes = [
        // срабатывает при вызове корня или /index.php
        '/' => 'hello',
        // срабатывает при вызове /about или /index.php/about
        '/about' => 'about',
        // динамические страницы
        '/page' => 'page'
    ];
    
    // возвращает путь запроса
    // вырезает index.php из пути
    function getRequestPath() {
        $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    
        return '/' . ltrim(str_replace('index.php', '', $path), '/');
    }
    
    // наш роутер, в который передаются маршруты и запрашиваемый путь
    // возвращает функцию если маршшрут совпал с путем
    // иначе возвращает функцию notFound
    function getMethod(array $routes, $path) {
        // перебор всех маршрутов
        foreach ($routes as $route => $method) {
            // если маршрут сопадает с путем, возвращаем функцию
            if ($path === $route) {
                return $method;
            }
        }
    
        return 'notFound';
    }
    
    // функция для корня
    function hello() {
        return 'Hello, world!';
    }
    
    // функция для страницы "/about"
    function about() {
        return 'About us.';
    }
    
    // чуть более сложный пример
    // функция отобразит страницу только если
    // в запросе приходит id и этот id равен
    // 33 или 54
    // [/page?id=33]
    function page() {
    
        $pages = [
            33 => 'Сага о хомячках',
            54 => 'Мыши в тумане'
        ];
    
        if (isset($_GET['id']) && isset($pages[$_GET['id']])) {
            return $pages[$_GET['id']];
        }
    
        return notFound();
    }
    
    // метод, который отдает заголовок и содержание для маршрутов,
    // которые не существуют
    function notFound() {
        header("HTTP/1.0 404 Not Found");
    
        return 'Нет такой страницы';
    }
    
    
    // Роутер
    // получаем путь запроса
    $path = getRequestPath();
    // получаем функцию обработчик
    $method = getMethod($routes, $path);
    // отдаем данные клиенту
    echo $method();


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

    4. Обойтись без него можно. Если каждая страница в вашем приложении будет являться отдельным файлом, который отвечает за отдачу информации.
    index.php
    about.php
    contact.php
    ...


    Это олдскульная структура, в новых проектах почти не применяется.
    Ответ написан
    13 комментариев
  • Как правильно создать индекс в ElasticSearch?

    onqu
    @onqu
    weasy
    1. Проверять ничего не надо, если данные не нужны, запрос на обновление всего документа аналогичен запросу на добавление. Запрос ниже либо создаст новый документ, либо полностью заменит существующий.

    PUT /компании/компания/{_id}
    {
        "навание": "SpaceX",
        "работники":  ...,
    }


    Если "hash" уникален для каждой копании, его можно использовать в качестве _id

    PUT /компании/компания/{hash}
    {
        ...
    }


    В более ранних версиях (до 1.5) можно было использовать alias для поля _id, которое может генерироваться автоматически:

    "mappings": {
        "компания": {
            // в текущей версии: 2.3 depricated - сказывалось на производительности
            "_id": {"path": "hash"},
            "properties": {
                "навание": {
                    "type": "string"
                },
                ...
            }
        }
    }


    2. В эластике нет, как таковых массивов, есть вложенные объекты. Любое поле документа может содержать множество значений, но значения должны быть одного типа. Тип может быть nested или object, nested позволяет производить более удобный поиск при множестве вложенных объектов.

    Если правильно понимаю, и должности разные, то будет удобнее использовать nested. Иначе object.

    "mappings": {
        "компания": {
            "properties": {
                "работники": {
                    "type": "nested",
                    "properties": {
                        "должность": {
                            "type": "string"
                        },
                        "имя": {
                            "type": "string"
                        }
                    }
                }
            }
        }
    }
    
    // создание/обновление
    PUT /компании/компания/{_id}
    {
        "название": "...",
        "hash": "...",
        "работники": [
            {
                "должность": "манагер",
                "имя": ["Анатолий", "Андрей"]
            },
            {
                "должность":  ["управляющий", "заместитель"]
                "имя": "Дмитрий"
            },
            {
                "должность": "кассир",
                "имя": ["Татьяна", "Анастасия"]
            },
        ]
    }
    
    // примерный поиск
    GET /компании/компания/_search
    {
        "query": {
            "nested": {
                "path": "работники",
                "query": {
                    "bool": {
                        "must": [
                            { "match": { "работники.должность": "управляющий" }},
                            { "match": { "работники.должность":  "кассир" }} 
                        ]
                    }
                }
            }
        }
    }
    Ответ написан
    8 комментариев
  • Утечка памяти при парсинге SAX методом(XML), что я зделал не так?

    onqu
    @onqu
    weasy
    Судя по всему, массив $output разрастается очень сильно, памяти не хватает. Решить можно разными путями:

    1. сделать цикл в методе execute с yield если php >= 5.5, но нужно будет следить за консистентностью массива $output, чтобы удалять отданные данные и дописывать новые
    2. обернуть в ArrayIterator, но также нужно будет писать другой код
    3. передавать в execute callback функцию, которая будет вызываться в методе endElements и если $name будет равен 'class', то есть при закрытии обертки

    private function endElements($parser, $name){
            $this -> deep --;
            if(!empty($name)) {
                $this -> element = null;
            }
    
            // $this->wrapper == 'class'
            if ($name === $this->wrapper && !empty($this->output)) {
                    // отдать данные в коллбэк
                    call_my_callback($this->output);
                    // очистить буфер
                    $this->output = [];
            }
        }


    ps. может возникнуть ошибка в методе characterData, в этот метод данные могут быть дописаны, например:
    fread вернул данные, которые оканчиваются на "<learner>Уче", как итог в данные парсером запишется "Уче", при следующем вызове в метод прилетит "ник1", и перезапишет "Уче".

    нужно делать что-то типа:

    private function characterData($parser, $data){
            if(!empty($data)) {
                if (in_array($this->element, $this->necessary_elements)) {
                    if (!isset($this->output[$this->element])) {
                        $this->output[$this->element] = '';
                    }
                    $this->output[$this->element] .= trim($data);
                }
            }
        }
    Ответ написан
    Комментировать
  • Как в codecption создать stub для метода контроллера?

    onqu
    @onqu
    weasy
    В этом случае удобнее mock, но перед этим лучше проверить, что метод вообще вызывается. Stub хорош для полной замены чего-либо.

    // аргументы конструктора MyController
    $constructorArgs = [
        'id', 
        Yii::$app,
    ];
    
    // заменяемые методы и свойства MyController
    $methods = [
        // метод, который тестируем
        // подойдет любой тип callable
        'methodToTest' => function() {
            $args = func_get_args();
            
            ... тестируем аргументы
        },
    ];
    
    $controllerMock = \Codeception\Util\Stub::construct(
        '\namespace\controllers\MyController',
        $constructorArgs,
        $methods
    );
    $controllerMock->run('action', []);
    Ответ написан
    2 комментария
  • Как протестировать action в консольном контроллере yii2?

    onqu
    @onqu
    weasy
    Здравствуйте.

    public function testMyConsoleController()
    {
        $appConfig = [];
        $this->mockApplication($appConfig);
        
        $params = ['foo', 'bar'];
        $controller = new MyConsoleController('id', Yii::$app);
        $result = $controller->run('action', $params);
    
        $this->assertEquals(0, $result);
    }
    Ответ написан
    4 комментария
  • Как поддерживать две версии приложения (платная и бесплатная)?

    onqu
    @onqu
    weasy
    Конечно, можно использовать 2 ветки, конечно, можно делать все в одной ветке и понатыкать везде #ifdef FOR_NISCHEBROD, FOR_REGULAR_CLIENT, FOR_RICH_ONE, конечно, можно расставить тэги, чтобы было проще искать эти места в будущем.

    Но, при увеличении объема логики придется прибегнуть к использованию шаманского бубна, ритуалу выстрела в свою ногу и мольбы праотцам. Добавлять/править логику в этих кусках будет очень непросто.

    Другой вариант.
    Делать приложение модульным, где основное приложение является лишь каркасом с базовой функциональностью, лежит в отдельной репе, тестируется отдельно от всего, и где модули это подключаемые расширения (Компоненты, DLC, LIB, Whatever), у которых есть API интерфейс для расширения функциональности основного приложения, и каждый лежит в своей репе.
    Более того, их можно будет тестировать, как вкупе, так и отдельно от основного приложения. При сборке указываем только требуемые расширения. Нэкст лэвэл - подключать расширения динамически, то есть без сборки с приложением.
    Ответ написан
    2 комментария
  • Как разделить домашнее и рабочее пространство?

    onqu
    @onqu
    weasy
    Простые решения не в моде. Всем нужна боль, никто даже не заметил, что она превратилась в зависимость :) Как то так? :)

    Пользуемся ноутом и не паримся, а на нем разворачиваются контейнеры с нужным окружением.
    Ответ написан
    Комментировать
  • Mongo: хранение комментариев и лайков в отдельной коллекции или в атрибутах самого файла?

    onqu
    @onqu
    weasy
    Если вы на 100% уверены, что размер документа не превысит лимит (если не ошибаюсь это 16мб), то хранить, как вложенный массив, очевидно будет удобнее. Иначе в отдельной коллекции. Но если хранить отдельно, нужен кеш общего количества лайков, так что нужно будет клепать эту логику.

    Плюс отдельного хранения еще и в том, что удобнее производить массовые обновления, если они требуются. При вложенном хранении уменьшается количество обращений в бд.
    Ответ написан
    3 комментария
  • Как лучше организовать отчет от RabbitMQ?

    onqu
    @onqu
    weasy
    Site в данном случае сам должен ходить за пирожками.
    8Dh9rkX.png
    Ответ написан
    3 комментария
  • Как избавиться от бота, который постоянно регистрируется?

    onqu
    @onqu
    weasy
    Чтобы у вас зарегистрироваться, каптчу вводить не надо. У вас валидация только на клиенте.
    Заполняем email, имя и ставим любое значение в textarea id="g-recaptcha-response" -> submit, profit.

    upd: или на сервере в коде неверная валидация
    upd2: запускаем прямо из консоли браузера, значения свойств input можно менять

    var input = {
      // имя
      name: 'Иванов',
      // email
      email: 'ivanov.no.captcha@mail.ru',
      //
      'g-recaptcha-response': 'whatever'
    };
    
    var formId = 'reg_form';
    var query  = '[name=email],[name=name],[id=g-recaptcha-response]';
    
    var form = document.getElementById(formId);
    var fields = form.querySelectorAll(query);
    
    for (var i = 0; i < fields.length; ++i) {
      var field = fields[i];
      var name = field.getAttribute('name');
      var value = input[name];
    
      $(field).val(value);
    }
    
    $(form).submit();
    Ответ написан
    Комментировать
  • Какой массив должен быть в $model->load()?

    onqu
    @onqu
    weasy
    Ключом выступает имя класса модели.

    [
       'ИмяКласса' => [
            'id' => '2'
            'type' => '2'
            'date' => '12/01/2011'
        ]
    ]


    Или без указания оного
    $data = [
        'id' => '2'
        'type' => '2'
        'date' => '12/01/2011'
    ];
    
    // второй параметр - пустая строка
    $model->load($data, '');


    Или можно использовать любой свой ключ, но его необходимо также указать явно.
    $data = [
       'мой ключ' => [
            'id' => '2'
            'type' => '2'
            'date' => '12/01/2011'
        ]
    ];
    
    $model->load($data, 'мой ключ');
    Ответ написан
    1 комментарий
  • Чем может быть полезен C++ веб разработчику?

    onqu
    @onqu
    weasy
    Чтобы сделать свой браузер и наблюдать с попкорном, как смертные будут пытаться оптимизировать свои творения под него.
    Ответ написан
    1 комментарий
  • Какие есть практические курсы\уроки по html5 + css3 + javascript?

    onqu
    @onqu
    weasy
    Ответ написан
    Комментировать