• Какой PHP-микро-фреймворк взять для простенького REST API с авторизацией, и чтобы не из "большой тройки"?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Symfony 4 в стандартной поставке— это микрофреймворк, серьезно
    Ответ написан
    2 комментария
  • Как на php ставить задачи в очередь например при парсинге сайта?

    @Flying
    Подобная задача решается через т.н. брокеры сообщений (Message broker). Есть несколько возможных вариантов, к примеру выделенные сервера, реализующие протокол AMQP (например RabbitMQ, ActiveMQ и другие), стриминговые сервера (к примеру Kafka), сервисы очередей (Amazon SQS и подобные), а также другие реализации паттерна PubSub, к примеру в Redis.

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

    В вашем сценарии скорее всего будет подразумеваться две очереди:
    1. Очередь задач на scraping. а которую подписываются множество worker'ов, осуществляющих непосредственный сбор данных
    2. Очередь с результатами сбора данных, куда worker'ы будут скидывать данные и на которую подписываются обработчики, решающие что же делать с данными дальше

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

    Для PHP существуют немало binding'ов к серверам, реализующим message brokering. Не знаю что именно на сервер вы выберете, поэтому навскидку приведу php-amqplib/php-amqplib для работы с AMQP (в первую очередь с RabbitMQ), enqueue/amqp-tools (также для AMQP) и superbalist/php-pubsub для реализации PubSub паттерна, к примеру через Redis.

    Вы не указали на чём написан ваш проект, но в целом также можно посмотреть в сторону компонента Messenger для Symfony, его можно использовать и вне Symfony и он даёт хорошую базу для реализации схем обмена сообщениями.
    Ответ написан
    3 комментария
  • Есть ли смысл, в такой шифровке?

    SagePtr
    @SagePtr
    Еда - это святое
    Если хотите хешировать пароль, то нет никакого смысла в велосипеде, используйте функции password_hash и password_verify
    Ответ написан
    Комментировать
  • Как делать пакеты как у Symfony?

    ghost404
    @ghost404
    PHP Developer
    У меня есть опыт такого подхода.

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

    Не думаю что у вас есть в это особая необходимость. Но вы можете разработать какие-то свои компоненты и вынести их в отдельные репозитории и устанавливать как зависимости. Можно даже сделать их open source и сообщество может вам помогать в их разработке и сопровождении.

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

    Таким образом у вас будет следующие git репозитоии:
    - ядро проекта
    - компонент блога
    - бандл блога
    - компонент мероприятий
    - бандл мероприятий
    - компонент деятелей
    - бандл деятелей
    - компонент аккаунта
    - бандл аккаунта
    и др. х2

    Разделение проекта на много независимых репозиториев это не есть плохо. Как и разделение проекта на микросервисы. Но у этого подхода есть существенный недостаток. Часто нужно внедрять новые фичи или править баги которые требуют внесения изменений в несколько репозиториев одновременно. Это доставляет массу неудобств. Особенно когда нужно проходить через этап релиза. Протестировать в проекте работу правок можно только после внесения их в бандл и релизе его, а зарелизить бандл можно только после его тестирования, а тестирование бандла требует тестирования и релиза компонента, а делать релиз компонента не убедившись как оно будет работать в конечном продукте нельзя. В общем релизный ад. Я проходил через него и не советую другим. Это осмысленно только для больших проектов с большой командой разработчиков.

    Как и сказал BoShurik, разделяйте проект на независимые модули и организуйте модули в папки и неймспейсы. Я пользуюсь этим методом уже несколько лет. Полет нормальный.

    App/Blog/Controller
    App/Blog/Entity
    App/Events/Controller
    App/Events/Entity


    Разработчики Symfony зря советуют не использовать бандлы. Ваши модули по сути и будут бандлы. Вы создаёте в модуле бандл в котором описываете метод интеграции модуля с фреймворком.

    App/Blog/AppBlogBundle
    App/Events/AppEventsBundle


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

    Касательно того как всё устроено конкретно в Symfony. Я довольно давно изучал этот вопрос и могу ошибаться в деталях.
    В Symfony когда-то использовался git submodules для компонентов. Но от этого решения отказались. Оно не очень удобное, особенно для конечного пользователя. В плане разработки это чуть удобнее чем отдельные пакеты на мой взгляд.
    Сейчас всё работает иначе. Есть основной репозиторий symfony/symfony. Все компоненты находятся в нем. Всё правки вносятся в него. В репозиториях компонентов никакой активности нет. При коммитете или пуше в ветки symfony/symfony запускается хук который на отдельном сервере SensioLabs запускается специальная утилита которая копирует все коммиты сделанные в компонентах из symfony/symfony в репозитории соответствующих компонентов. Мне как-то попадался сайт SensioLabs на котором можно было скачать эту утилиту.
    Суть в том, что это не репозиторий Symfony состоит из репозиториев компонентов. Это компоненты Symfony по сути выдранные куски кода из основного репозитория.

    Я когда-то тоже хотел сделать приложение с выделенными модулями в отдельные репозитории как у Symfony, но разобравшись в вопросе я пришел к выводу, что мне это не подходит. И вам думаю тоже не подойдёт.
    Ответ написан
  • Можно ли указать два типа для аргумента?

    @green_goo
    Нет, только если в аннотациях
    /**
     * @param string|int $a
     */
    function test($a) {}
    Ответ написан
    Комментировать
  • Что почитать про правильное проектирование своих приложений?

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

    В целом, если вы хотите получше разобраться в паттернах проектирования - можно брать любую хорошую книжку по этой теме (те же Gang of Four, Мартин Фаулер (на русском) и т.п., предложенная Ярослав ссылка тоже выглядит весьма достойно), изучать, а затем пытаться найти эти паттерны в коде популярных и хорошо написанных проектов.

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

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

    php666
    @php666
    PHP-макака
    Есть метод калькуляции заказа. Метод нужен во многих местах.
    в этом и ошибка - все в кучу. Надо сделать, что бы калькуляция была не черным ящиком, а настраиваемым удобным интерфейсом:
    $discountCalculator = new discountCalculator(\ListItems $items);
    $discountCalculator->addCondition(new SimpleDiscount());
    $discountCalculator->addCondition(new BlackFridayDiscount());
    $discountCalculator->addCondition(new ByDateDiscount('last day of this month'));
    $discountCalculator->addCondition(new ApiDiscount());
    $discountCalculator->calculate();
    Ответ написан
    6 комментариев
  • Чем отличаются миграции от доктрины?

    @vanillathunder
    Миграция осуществляет изменения в бд, а доктрина это ORM
    Ответ написан
    3 комментария
  • Как удалить из массива все элементы до определенного?

    @DanKud
    array_slice + array_search

    $date = [
        '10.04.2019',
        '11.04.2019',
        '12.04.2019',
        '13.04.2019',
        '14.04.2019',
        '15.04.2019',
        '16.04.2019',
        '17.04.2019',
        '18.04.2019'
    ];
    
    $today = date('d.m.Y');
    $date = array_slice($date, array_search($today, $date));
    print_r($date);
    
    /*
    Array
    (
        [0] => 15.04.2019
        [1] => 16.04.2019
        [2] => 17.04.2019
        [3] => 18.04.2019
    )
    */
    Ответ написан
    Комментировать
  • Нужно ли разделять модель с выводом данных и поиском?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    1. Да стоит, это похоже на repository
    $searchCriteria = new CustomerSearchCriteria;
    $searchCriteria->setByName("москва");
    $customers = $customerRepository->findByCriteria($searchCriteria);//->CustomerCollection

    2. Методы относятся к методам репозитория: $customerRepository->save($customer);//save это create и update в одном флаконе
    Ответ написан
  • Можно ли использовать простые функции внутри класса?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Да, можно.
    Ответ написан
    Комментировать
  • Почему не до конца работает связь?

    Делайте дамп $notice->getUser(), там всё будет. Это ленивая загрузка доктрины
    Ответ написан
    3 комментария
  • Как рассчитать значение поля на основании предыдущих документов?

    @Urukhayy
    В Mongo Aggregation можно сделать через временный массив.

    1. Сортируем все документы в порядке убывания rating и active (позиция будет расти сверху вниз)
    2. $push всех отсортированных документов во временный массив data
    3. Создание нового массива arr и внесение в него всех элементов массива data в том же порядке, но добавляя поле position, которое формируется исходя из положения объекта в массиве data ($indexOfArray)
    4. Разбиение ($unwind) массива arr на обычный список документов, но уже отсортированный и с проставленными position
    5. Преобразование документов после разбиения к нужному виду

    db.getCollection('testo').aggregate([
        { $sort: { rating: -1, active: -1 } },
        { $group: { _id: null, data: { $push: "$$ROOT" } } },
        {
            $project: {
                arr: {
                    $map: {
                        input: "$data", as: "e", in: {
                            rating: "$$e.rating", 
                            active: "$$e.active", 
                            position: {
                                $add: [{ $indexOfArray: ["$data", "$$e"] }, 1]
                            }
                        }
                    }
                }
            }
        },
        { $unwind: "$arr" },
        { $project: { 
            _id: 0,
            rating: "$arr.rating", 
            active: "$arr.active", 
            position: "$arr.position" } }
    ])


    Если версия MongoDB довольно старая, то придется вручную вписывать все поля, которые нужно учитывать в массиве. Если же версия новая, можно дополнить действием $mergeObjects. Также есть вариант использовать более сложные схемы ($arrayToObject, $objectToArray) для сохранения полей при переходе массива из одного состояния в другое
    Ответ написан
    1 комментарий
  • Как использовать Doctrine вне Symfony?

    padlyuck
    @padlyuck
    Setup::createAnnotationMetadataConfiguration($entitiesPaths, $isDevMode, null, null, false);
    для доктрины до версии 3.0(в третьей версии вроде бы сделают по нормальному). Скорее всего проблема в том, что в проекте на Symfony вы в аннотациях пишете что-то вроде @ORM\Entity, а для этого чистой доктрине нужно подсунуть нормальны AnnotationReader, а не Simple который по дефолту создается.
    Ответ написан
    1 комментарий
  • Как получить значение из POST запроса?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Если тег Symfony не просто так:
    $request->request->get('user')[name];

    Источник: https://symfony.com/doc/current/components/http_fo...
    Ответ написан
    1 комментарий
  • Как передать объект БД в класс?

    ThunderCat
    @ThunderCat Куратор тега PHP
    {PHP, MySql, HTML, JS, CSS} developer
    Классический DI
    class App{
      protected $db;
      public function __construct($db){
        $this->db = $db;
      }
    }
    Ответ написан
    7 комментариев
  • Symfony 4.2, какая производительность autowiring'а через @required аннотацию?

    @Flying
    Поскольку контейнер в Symfony компилируемый - на runtime производительность это не влияет никак. Если хотите проверить - загляните в то во что превращается код создания вашего сервиса в контейнере, всё это лежит в var/cache, там будет обычный setLogger($this->get('logger')) или что-нибудь типа этого.
    Ответ написан
    Комментировать
  • Как сделать на странице динамическую подгрузку, статического контента?

    voronkovich
    @voronkovich
    Если контент статический, вы можете просто вставить его HTML-код в шаблон. В иных случаях лучше использовать HMVC: How to Embed Controllers in a Template

    В демо приложении есть два примера, иллюстрирующих вставку блоков с контентом: https://github.com/symfony/demo/blob/bbe5180a8c3b6... и https://github.com/symfony/demo/blob/bbe5180a8c3b6...

    В последнем примере также используется ESI для кэширования блока.

    Также, если блок статический, то для него можно не создавать отдельный контроллер, а использовать TemplateController: How to Render a Template without a custom Controller
    Ответ написан
    1 комментарий
  • Как в symfony 4 разбить большую форму на вкладки?

    voronkovich
    @voronkovich
    Нужно просто вручную вывести поля формы в коде вкладок в шаблоне (How to Control the Rendering of a Form). Если это админка, и вы используете bootstrap, можете использовать табы: https://getbootstrap.com/docs/4.0/components/navs/#tabs.
    Основная проблема при использовании вкладок заключается в том, что вам нужно, чтобы после отправки формы текущая вкладка осталась открытой. Поэтому, нужно сохранять идентификатор текущей вкладки в хранилище браузера. Пример можете найти здесь: How to keep the current tab active on page reload ....

    Примерно так это может выглядеть:

    {% extends 'base.html.twig' %}
    
    {% block body %}
    <ul class="nav nav-tabs" role="tablist">
        <li class="nav-item">
            <a class="nav-link active" href="#general-tab" data-toggle="tab" role="tab" aria-selected="true">
                General
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#security-tab" data-toggle="tab" role="tab" aria-selected="false">
                Security
            </a>
        </li>
    </ul>
    <div class="tab-content">
        {{ form_start(form) }}
        {{ form_errors(form) }}
        <div class="tab-pane active show" id="general-tab" role="tabpanel" aria-labelledby="general-tab">
            {{ form_row(form.field1) }}
            {{ form_row(form.field2) }}
        </div>
        <div class="tab-pane" id="security-tab" role="tabpanel" aria-labelledby="security-tab">
            {{ form_row(form.field3) }}
            {{ form_row(form.field4) }}
        </div>
        <button type="submit">Submit</button>
        {{ form_end(form) }}
    </div>
    {% endblock %}
    
    {% block javascripts %}
    {{ parent() }}
    <script charset="utf-8">
    $(function() {
        $('a[data-toggle="tab"]').on('show.bs.tab', function(e) {
            localStorage.setItem('activeTab', $(e.target).attr('href'));
        });
    
        var activeTab = localStorage.getItem('activeTab');
    
        if (activeTab) {
            $('.nav-tabs a[href="' + activeTab + '"]').tab('show');
        }
    });
    </script>
    {% endblock javascripts %}
    
    {% block stylesheets %}
    {{ parent() }}
    <style type="text/css" media="screen">
    .tab-pane:not(.show) {
        display: none;
    }
    .tab-pane.show {
        display: block;
    }
    </style>
    {% endblock  %}
    Ответ написан
    Комментировать