Задать вопрос
  • Как объединить запросы в транзакцию?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Поскольку быстрым поиском готовый ответ на Тостере не находится, стоит написать канонический.

    Сначала общая информация:

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


    Самым простым вариантом будет заключить запросы между вызовами beginTransaction() и commit(), как показано например в документации к последнему.
    $db->beginTransaction();
    $db->prepare("UPDATE `tab1` SET `col` = ?")->execute($data1);
    $db->prepare("UPDATE `tab2` SET  `col` = ?")->execute($data2);
    $db->prepare("UPDATE `tab3` SET  `col` = ?")->execute($data3);
    $db->commit();

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

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

    В редких случаях, когда после отката транзакции предполагается дальнейшее выполнение кода, можно заключить код транзакции в try-catch и откатить её вручную.

    Важно помнить некоторые особенности.
    • в mysql не все движки таблиц поддерживают транзакции. впрочем ,учитывая что innodb является движком по умолчанию уже лет 20, это вряд ли будет проблемой
    • запросы на изменение страктуры таблиц автоматически коммитят стартовавшую транзакцию, то есть их следует избегать. Кажется, в новых версиях какие-то уже не коммитят, но я предпочитаю избегать всё равно.


    Также желательно помнить, что в любом более-менее сложном коде очень быстро появляются вложенные транзакции, а PDO при попытке стартовать транзакцию при уже открытой, выбросит исключение, что, соответственно, приведёт к откату родительской (и это гораздо лучше поведения MySQL по умолчанию, которая автоматически коммитит старую). И имеет смысл накидать простой кодик, который считает вложенные транзакции, и не стартует, если уже был старт, а при коммите вычитает вложенность, а реально коммитит только если вложенности не осталось. Что-то вроде кода из комментариев к beginTransaction(), подравняв его напильником
    class \MyPDO extends \PDO
    {
        protected $transactionCounter = 0;
    
        public function beginTransaction()
        {
            if($this->transactionCounter++ === 0) {
                return parent::beginTransaction();
            }
        }
        public function commit()
        {
            $this->transactionCounter--;
            if($this->transactionCounter === 0) {
                return parent::commit();
            }
        }
        public function rollback()
        {
            $this->transactionCounter = 0;
            return parent::rollback();
        }
    }

    разместив его либо прямо в PDO, либо в своем враппере.
    Ответ написан
    3 комментария
  • Какой должен быть алгоритм синхронизации с 1С?

    @resolution07
    PHP Backend
    Механизм обмена товарами и заказами, а так же остатками и ценами есть в типовом обмене с Битрикс. Поищите в сети документацию к Битрикс. Там весть алгоритм от начала до конца расписан + есть схема. Может вам это поможет
    Ответ написан
    Комментировать
  • Какой должен быть алгоритм синхронизации с 1С?

    @dimas103
    Варианты от простого к сложному или на вкус и цвет
    1 в 1с использовать обмен с сайтом только для регистрации событий для обмена. Написать свой обмен в 1с и соответственно на стороне сайта
    2 сменить платформу на Битрикс (в нем обмен из коробки с 1с встроен)
    3 взять Битрикс настроить с ним обмен и посмотреть что он возвращает и сделать по аналогии на своем сайте
    Ответ написан
    2 комментария
  • Какой должен быть алгоритм синхронизации с 1С?

    borisdenis
    @borisdenis
    Ленив и вреден...
    Вариант 2 точно не стоит использовать, так как:
    1. Вы ответили success, а значит данные успешно получены, а потом вдруг при обработке файла поняли, что он поломан...
    2. Если всё собирать в один файл, то его размер может вырасти до неприличия и парсер при его обработке умрет, а вы уже ответили success и попробуй пойми какая част данных в базе, а какая не успела обработаться...
    Ответ написан
    Комментировать
  • Как тестировать запросы и ответы из базы данных через phpunit?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Мне кажется, что найденные варианты работают не так. Используются они оба, а какой именно - зависит от того, что именно вы тестируете - запрос или ответ.

    Если говорить про "запросы к БД", то для таких тестов "массив с данными" использовать просто глупо. Это получится какой-то формальный тест: "проверяем, что метод возвращает массив из трех строк - и тут же возвращаем этот самый массив". В чем смысл? Если вы тестируете запрос к БД, то и надо тестировать запрос к БД. По-другому никак.

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

    И сюда же использование для тестов БД другой системы. Например основная БД MySQL, а для тестов используется Sqlite. Тут сразу можно сказать, что это профанация. Различие даже в какой-то одной настройке БД может повлиять на результаты запроса (и теста как следствие) - а тут и вовсе используется совсем другая БД.

    С другой стороны, работу с БД скорее стоит тестировать не в юнит тестах, а скажем в интеграционных. Но не будем углубляться.


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

    Здесь нелишне будет напомнить название доклада с одной из последних конференций по пхп. Дословно не помню, но что-то вроде "Делайте методы как можно короче. Ваши тесты скажут вам спасибо." Собственно, именно при тестировании очень быстро начинаешь понимать удобство атомарных (то есть выполняющих какую-то одну простую операцию) методов. А без тестов пройдёт довольно много времени, пока не понадобится этот длинный метод отрефакторить, и по итогам менять кучу кода, который его вызывает.

    Так что разбивайте ваши длинные методы на мелкие, и тогда вопрос, как их тестировать, в большинстве случаев отпадёт сам собой.
    Ответ написан
    1 комментарий
  • Какие требования к VPS/VDS для PHP-фреймворков?

    Adamos
    @Adamos
    Просто в качестве иллюстрации химерности "минимальных требований" - тот самый тариф на Таймвебе, два сайта на Ларавель и Октябре, который суть та же Ларавель. Показания панели Таймвеба по нагрузке на процессор:
    6542324e23f34845203856.png
    Ответ написан
    5 комментариев
  • Входит ли текущая дата в интервал, если года разные?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    $date = new DateTimeImmutable();
    $isInAcademicYear = (
        $date <= new DateTimeImmutable('31 May') ||
        $date >= new DateTimeImmutable('01 September')
    );
    var_dump($isInAcademicYear); // true
    Ответ написан
    1 комментарий
  • Golang подходит ли для создания сайтов?

    @MadridianFox
    Web-программист, многостаночник
    1) Go компилируемый, само собой скорость выше, но если вы на маленьком сайте упираетесь в скорость исполнения кода, то проблема скорее в коде, и выбор более быстрого языка избавит вас только от симптома, но не от болезни
    2) на seo влияет только то ЧТО вы отдаёте пауку. Не важно на каком языке пишется программа, которая отдаёт html
    3) Программа написанная на go сама работает как сервер. Более того, это не какой-то сервер приложений, как например tomcat, нет, вы сами будете писать цикл обработки входящих соединений - т.е. вам необходимо этот самый серер реализовать. В отличие от php+apache, где в качестве сервера выступает apache, который при необходимости вызывает php. Ставить ли apache или nginx перед go сервером вы решаете сами.
    4) Поддерживает. На скорость работы СУБД не влияет то, на каком языке написана программа, которая к ней обращается.
    5) Фреймворков уровня Yii2 или Symfony не наблюдается. Ситуация такая же как и с NodeJS - что-то есть, но комбайнов нет.
    6) Если что-то может слушать соккет - на этом можно написать сайт. Любой. Другое дело, что сайты визитки на Go не пишут (только ради забавы), а крупные сайты вообще имеют сложную архитектуру, такую что язык уже не имеет значения.
    7) Для php разработчика - да. Как минимум вы меняете скриптовый язык на компилируемый. Это значит что любое изменение кода требует компиляции, остановки работающего сервера, загрузки бинарника на его место и запуск. Кроме того вы меняете Stateless модель работы кода на полноценно работающую программу. Если на php у вас скрипт запускался заново при каждом http запросе, то программа на go запускается один раз и после этого принимает множество запросов.
    Ответ написан
    10 комментариев
  • Как реализовать такой слайдер?

    @MagicMight
    no magic quotes
    Ответ написан
    Комментировать
  • Как найти и заменить текст в базе данных mysql?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    UPDATE articles SET text = REPLACE(text, '8-927-123-45-67', '8-927-123-76-54') WHERE text LIKE '%8-927-123-45-67%'
    Ответ написан
  • Как обработать несколько массивов для записи в БД?

    @Nc_Soft
    foreach($_POST['orange'] as $k=>$orange) {
         $pink = $_POST['pink'][$k];
         $blue = $_POST['blue'][$k];
         //do something with $orange, $pink, $blue
    }
    Ответ написан
    1 комментарий
  • Как сделать плавную прокрутку страницы до якоря?

    BedwaRe
    @BedwaRe
    Пиши код
    $("body").on('click', '[href*="#"]', function(e){
    	var fixed_offset = 100;
    	$('html,body').stop().animate({ scrollTop: $(this.hash).offset().top - fixed_offset }, 1000);
    	e.preventDefault();
    });

    Во-первых, асинхронно (например, у меня отзывы подгружались с другого сервиса, с задержкой несколько секунд). Во-вторых, для всех ссылок, которые содержат символ # (являются якорями). В-третьих, у меня в проекте было фиксированное меню сверху, поэтому при прокрутке прямо по якорю часть контента перекрывалась, для решения этой проблемы предлагаю установить fixed_offset равный высоте фиксированного меню (опционально).
    Ответ написан
    8 комментариев
  • Если товар относится к нескольким категориям в WooCommerce?

    HeadOnFire
    @HeadOnFire
    PHP, Laravel & WordPress Evangelist
    Почему бы не использовать product attributes, которые уже как бы таксономия, и предоставляет все что нужно. Кроме того, является интегральной частью WooCommece. И пермалинки есть. Подробнее - в уточнениях к вопросу.
    Ответ написан
    Комментировать
  • Как реализовать новый Wordpress в поддиректории, но со связью с основным?

    kumaxim
    @kumaxim
    Web-программист
    Смотри в сторону Wordpress Multisite
    Ответ написан
    Комментировать
  • Как реализовать правильную структуру ссылок и подключений?

    Denormalization
    @Denormalization
    Привет из 2016 года. За то время, что прошло с 2000, многое поменялось.

    Для подключения файлов используется autoload и всякие модные стандарты (вроде PSR-4).
    Для подключения шаблонов тоже используют всякие шаблонизаторы, которые умеют работать с layout и partials.

    Если вы только недавно очнулись из анабиоза, советую почитать что такое composer/twig/blade/psr-4
    Ответ написан
    Комментировать
  • Как содержимое input записывать в БД из цикла PHP?

    ZLOFENIX
    @ZLOFENIX
    Абсолютно поехавший
    Страшный код.
    Ну допустим так:
    foreach($chain as $cur)
    echo "<input type=text name='arr[{$cur[1]}]' value='{$cur[2]}'>";


    Ну и потом:
    foreach($_POST['arr'] as $k=>&$v)
    $k - имя, $v - значение с формы.
    Ответ написан
    1 комментарий
  • Как содержимое input записывать в БД из цикла PHP?

    @asd111
    У вас sql injection в коде. Используйте PDO или prepared statements.
    Ответ написан
    1 комментарий
  • Как записывать рефералов в БД по счетчику?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    Родители по id пользователя:
    SELECT `t2`.`name`
        FROM `mp_tree` AS `t1`
        JOIN `mp_tree` AS `t2`
            ON `t1`.`path` LIKE CONCAT(`t2`.`path`, '.%')
        WHERE `t1`.`id` = :id
        ORDER BY `t2`.`path` DESC

    Родители по path пользователя:
    SELECT `name`
        FROM `mp_tree`
        WHERE :path LIKE CONCAT(`path`, '.%')
        ORDER BY `path` DESC

    Индекс использоваться не будет, поскольку `path` используется внутри функции.
    Я бы сделал на Nested Set, там вставка будет работать дольше, но выборка быстрее.
    Ответ написан
    3 комментария
  • Как записывать рефералов в БД по счетчику?

    petermzg
    @petermzg
    Самый лучший программист
    А зачем записываться за пригласившим?
    Добавьте в таблицу пользователей поле указывающее на пригласившего.
    Ответ написан
    9 комментариев
  • CMS для сайта с личными кабинетами?

    zooks
    @zooks
    Frontend
    Довольно странный вопрос. Нужно работать с той CMS, с которой вы уже знакомы. Я например делал бы на MODX.
    Ответ написан
    1 комментарий