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

    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 комментария
  • Как в Google Таблицах настроить установку даты и времени без обновлений при каждом клике?

    Geleoss
    @Geleoss
    Любитель таблиц
    Используйте следующую фичу.
    =IF(F1; LAMBDA(x; x)(NOW()); )
    где F1 - адрес ячейки с флажком
    Ответ написан
    1 комментарий
  • Как узнать, кто подключался к серверу и были ли осуществлена передача файлов по сети?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Включите аудит доступа. Проще всего он включается через политику.
    Ответ написан
    Комментировать
  • Как сделать аутентификацию на сайте через телеграм?

    CoDeR2006
    @CoDeR2006 Автор вопроса
    TS
    И так, для меня в будущем, и для тех кто это нагуглил:

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

    Интегрируем виджет в приложение react + ts:
    (это если вам надо что бы был не редирект, а вызов функции со стороны telegram)
    import { useEffect } from 'react'
    import { ITelegramUser } from '../../../../types/telegram/api-telegram-user.ts'
    
    declare global {
      interface Window {
        onTelegramAuth: (user: ITelegramUser) => void
      }
    }
    
    export const ButtonTelegramAuth = () => {
      useEffect(() => {
        const button = document.createElement('script')
        button.async = true
        button.src = 'https://telegram.org/js/telegram-widget.js?22'
        button.setAttribute('data-telegram-login', 'name_bot')
        button.setAttribute('data-size', 'large')
        button.setAttribute('data-radius', '20')
        button.setAttribute('data-onauth', 'onTelegramAuth')
    
        document.body.appendChild(button)
    
        window.onTelegramAuth = function (user) {
          alert(
            'Logged in as ' +
              user.first_name +
              ' ' +
              user.last_name +
              ' (' +
              user.id +
              (user.username ? ', @' + user.username : '') +
              ')'
          )
        }
    
        return () => {
          document.body.removeChild(button)
        }
      }, [])
    
      return <div id="telegram-widget-container"></div>
    }


    если нужен все таки редирект мы добавляем такое поле:
    script.setAttribute('data-auth-url', 'https://site.pw');

    и удаляем это:
    button.setAttribute('data-onauth', 'onTelegramAuth')
    
    declare global {
      interface Window {
        onTelegramAuth: (user: ITelegramUser) => void
      }
    }
    
     window.onTelegramAuth = function (user) {
          alert(
            'Logged in as ' +
              user.first_name +
              ' ' +
              user.last_name +
              ' (' +
              user.id +
              (user.username ? ', @' + user.username : '') +
              ')'
          )
        }


    И так объясняю в чем задумка:
    В ваше приложение react интегрируется все тот же виджет, но правильным образом. После нажатия на виджет происходит авторизация пользователя (он вводит свои данные / просто нажимает войти). Потом телеграм передает все его данные вам (в функцию - №1 способ / по get параметрам в url). Если вы делегируете
    регистрацию на сервер (а так правильно) то выбираете 2 способ. Сервер получает данные валидирует их и заносит в БД, затем делает редирект вашего пользователя к вам обратно на front, и заносит token (или что там еще не разобрался) в HttpOnly Ckookie. Ну а далее в моем случае Guard будет проверять есть этот token или нет, годен он или нет.

    Передаю привет Максиму в будущем. Как дела Макс ?
    Ответ написан
    11 комментариев
  • Восстановление жестких дисков. Куда обращаться в Москве?

    Zettabyte
    @Zettabyte
    Проф. восстановление данных ▪ Вопрос? См. профиль
    Восстановление жестких дисков. Куда обращаться в Мск?

    Будем рады помочь в Москве: https://rlab.ru/hddrecovery/
    Работаем семь дней в неделю, находимся в центре, одна минута от метро. Также есть курьер. Занимаемся только data recovery с 2002 года, диагностика всегда бесплатная.

    На случай вопросов, в разделе "Контакты" есть полный набор способов связи (в т.ч. для консультаций с нашими инженерами) - от традиционных до соц. сетей и мессенджеров. Основные продублированы у меня в профиле.
    Либо можете писать мне.

    Полистал хабр с грустными историями когда плохие мастера все портят - не хочу так

    Увы, но сейчас это бич отрасли. Шарлатаны-однодневки вешают на свой сайт стоковые фото фармацевтических лабораторий и рассказывают про "восстановление данных от 1500 / 999 / 499 рублей".

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

    какая вилка цен вообще на восстановление данных?

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

    Для того, чтобы примерно оценить стоимость, нужно знать модель диска, что с ним случилось, что делали после этого, и симптомы.
    Люди обещающие фиксированную сумму, не видя диск, или даже на 100% гарантирующие восстановление заочно, часто обманывают, стараясь заманить вас к себе любыми способами.

    На всякий случай, вот наши цены на восстановление данных. Прайс-лист сделали максимально подробным.
    Оптимальный вариант - это диагностика в специализированной компании. В нормальных она бесплатная. Мы всегда стараемся делать её при клиенте, вы в это время можете, например, попить чай или кофе у нас на диванчике.
    Ответ написан
    Комментировать