Задать вопрос
  • Как одновременно сделать две записи в разных таблицах в Laravel?

    Оборачиваем всё в транзакцию, да и всё. И тогда создадутся либо обе записи, либо ни одной. Так мы не расплодим сироток в случае ошибочных данных.

    Модели:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable;
    
    class User extends Authenticatable
    {
        use HasFactory, Notifiable;
    
        /**
         * @var array<int, string>
         */
        protected $fillable = [
            'name',
            'email',
            'password',
        ];
    
        /**
         * Получить данные пользователя, связанные с этим пользователем.
         */
        public function userData()
        {
            return $this->hasOne(UserData::class);
        }
    }


    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    
    class UserData extends Model
    {
        use HasFactory;
    
        /**
         * @var array<int, string>
         */
        protected $fillable = [
            'phone_number',
        ];
    
        /**
         * Получить пользователя, которому принадлежат эти данные.
         */
        public function user()
        {
            return $this->belongsTo(User::class);
        }
    }


    Создание записей

    use App\Models\User;
    use App\Models\UserData;
    use Illuminate\Support\Facades\DB;
    use Illuminate\Support\Facades\Hash;
    
    DB::transaction(function () {
        // 1. Создаем пользователя (User)
        $user = User::create([
            'name' => 'Вася Пупкин',
            'email' => 'uasya@fsb.ru',
            'password' => Hash::make('12345'),
        ]);
    
        // 2. Создаем данные пользователя (UserData) и связываем их с пользователем
        $user->userData()->create([
            'phone_number' => '112',
        ]);
    });


    P.S. ИМХО: ненавижу Eloquent и весь паттерн Active Record. Как по мне, вместо него лучше уж просто чистым SQL пользоваться. А вот если нужна мощная ORM, то уж лучше воспользоваться Doctrine. Там вообще такой проблемы нет, и всё делается автоматически.
    Ответ написан
    4 комментария
  • Неточность формулировки в книге или же я не понимаю?

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

    Поэтому, в Go не надо зазубривать, какие операции что возвращают, а мы просто глядим в нашей мега-IDE, что конкретно нам возвращает time.Now()
    А возсращает она нам очень простую структурку time.Time

    type Time struct {
        wall uint64
        ext  int64
        loc  *Location
    }


    И что мы видим? Что из ссылок в этой структуре только loc, который занимается исключительно часовыми поясами, а само время хранится в обычных целочисленных переменных без всяких ссылок. А т.к., как мы говорили ранее, в Go всегда всё копируется, то внезапному указателю (ссылке) просто неоткуда взяться.

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

    Из всего вышеописанного мы делаем вывод, что не все книжки одинаково полезны даже для подтирания, не то что для изучения.

    Я рекомендую вам самый лучший инструмент для первоначального вкатывания в Go - это официальный курс "A Tour of Go", где вы в интерактивном режиме изучите базовые основы языка за пару дней. А дальше уже можно смотреть на книги, и уже самому находить в них такие вот несуразности. Не стесняйтесь преходить прямо в IDE на типы или определения функций, чтобы самому смотреть, что же там возвращается. Так как, на наше счастье, Go - это строго-типизированный язык, то никаких сюрпризов и подвохов там не будет.

    P.S. Нашёл отличную статью со списком материалов в дополнение: https://habr.com/ru/companies/selectel/articles/929858/
    Ответ написан
    2 комментария
  • Как прижать нижние колонки к низу родительского блока?

    К сожалению, без хака тут не обойтись. Но если получен результат, то кому какое дело? Берем волшебную палочку и достаём кролика из шапки. Сплошная иллюзия и никакого мошенничества.

    Ответ написан
    Комментировать
  • Можно ли пользоваться двумя аккаунтами в Gitlab одновременно?

    Опишу, как это сделать на Маке и Линуксе. Про Винду даже не спрашивайте, уже лет 7 как не помню.

    Для работы с двумя аккаунтами GitLab с одной машины требуется настроить Git для использования разных SSH-ключей. Каждому аккаунту будет соответствовать свой ключ.

    1: Создание отдельных SSH-ключей

    Для каждого аккаунта GitLab необходим уникальный SSH-ключ. Если стандартный ключ (`~/.ssh/id_rsa`) уже существует, его можно оставить для первого аккаунта. Для второго аккаунта создаётся новый.

    В терминале выполняется команда для генерации нового ключа. В ней `"email_второго_аккаунта@example.com"` заменяется на почту, привязанную ко второму аккаунту GitLab. При запросе имени файла нужно указать уникальное имя, чтобы не перезаписать существующие ключи.

    # Имя id_gitlab_work можно заменить на любое другое
    ssh-keygen -t ed25519 -C "email_второго_аккаунта@example.com" -f ~/.ssh/id_gitlab_work


    В результате у нас будет две пары ключей:

    • `~/.ssh/id_rsa` и `id_rsa.pub` (для первого аккаунта)
    • `~/.ssh/id_gitlab_work` и `id_gitlab_work.pub` (для второго)


    2: Добавление SSH-ключей в аккаунты GitLab

    Далее публичные части ключей (`.pub`) добавляются в соответствующие аккаунты GitLab.

    Для первого аккаунта это будет ~/.ssh/id_rsa.pub
    Для второго аккаунта ~/.ssh/id_gitlab_work.pub

    Кликаем на аватарку в gitlab, выбираем "Edit Profile" и в секции "SSH Keys" обоих аккаунтов добавляем соответствующие им публичные ключи

    3: Настройка SSH-клиента

    Чтобы система знала, какой ключ для какого репозитория использовать, настраивается файл конфигурации SSH. В нём создаются псевдонимы для `gitlab.com`.

    1. Нужно открыть или создать файл `~/.ssh/config`.

    Если его нет, создаём:
    touch ~/.ssh/config

    2. Открываем файл, добавляем конфигурацию, которая создаёт два «хоста», `gitlab.com-personal` и `gitlab.com-work`, которые оба ссылаются на `gitlab.com`, но используют разные файлы ключей.

    # Первый аккаунт (например, личный)
    Host gitlab.com-personal
      HostName gitlab.com
      User git
      IdentityFile ~/.ssh/id_rsa
      IdentitiesOnly yes
    
    # Второй аккаунт (например, рабочий)
    Host gitlab.com-work
      HostName gitlab.com
      User git
      IdentityFile ~/.ssh/id_gitlab_work
      IdentitiesOnly yes


    4: Настройка локальных репозиториев

    Теперь необходимо обновить URL-адреса удалённых репозиториев в локальных проектах, чтобы они использовали созданные псевдонимы.

    1. В директории проекта для первого аккаунта выполняется команда для изменения URL.
    Проверить текущий адрес: git remote -v
    Заменить `gitlab.com` на псевдоним gitlab.com-personal:

    git remote set-url origin git@gitlab.com-personal:username/repo.git


    2. В директории проекта для второго аккаунта выполняется аналогичная команда, но с другим псевдонимом:

    git remote set-url origin git@gitlab.com-work:otheruser/other-repo.git


    При клонировании новых репозиториев следует сразу использовать адрес с нужным псевдонимом:

    # Клонирование с первого аккаунта
    git clone git@gitlab.com-personal:username/repo.git
    
    # Клонирование со второго аккаунта
    git clone git@gitlab.com-work:otheruser/other-repo.git


    5: (Рекомендуется) Настройка автора коммитов

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

    В репозитории для первого аккаунта:

    git config user.name "Имя для первого аккаунта"
    git config user.email "email_первого_аккаунта@example.com"


    В репозитории для второго аккаунта:

    git config user.name "Имя для второго аккаунта"
    git config user.email "email_второго_аккаунта@example.com"


    После завершения настройки, при выполнении `git push` из разных директорий, Git будет автоматически использовать соответствующий ключ и данные автора.
    Ответ написан
    1 комментарий
  • Стоит ли верстать портфолио с сайтами на английском?

    А вы верстайте обе версии.
    1. Верстаете английскую версию (потому что английский знают почти все, а русский знают не только лишь все)
    2. Английскую версию вёрстки загружаете в любой ИИ, и он вам за секунду делает отличный перевод, который вам останется только немножечко подчистить. По моим наблюдениям ИИ щелкает фронтенд как семечки.
    Ответ написан
    Комментировать
  • Какую выбрать cms для интернет магазина с 10-30 тыс товаров?

    Всегда исходите от бизнеса. В зависимости от того, когда магазин нужен вашему бизнесу, у вас есть 2 варианта:

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

    2. Если же вы уже упускаете значительную прибыль, магазин будет крупной долей бизнеса и он должен был быть ещё вчера, то берите что угодно, ваша задача продавать товары, а не выбирать самое лучшее и вылизывать его. Поэтому, берите то, что можно быстро запустить, у чего есть миллионы разработчиков и очень широкое распространение. И тут оптимальным будет Wordpress + WooCommerce. Если же именно интеграция с учётной программой является камнем преткновения, то выберите что-то, что имеет самую адекватную интеграцию с вашей программой. Наклепайте тему, за которую не будет очень сильно стыдно, и продавайте, продавайте, продавайте... А когда заработаете денег, уже можно подумать о том, чтобы вернуться к п.1.
    Ответ написан
    3 комментария
  • Обновление версии PHP с 7.4.33 до 8.3 насколько необходимо?

    В первую очередь надо смотреть на то, когда заканчивается поддержка у той или иной технологии. И у PHP 7.4 она закончилась ещё в 2022 году.

    Это означает, что дыры в безопасности уже никто чинить не будет. А там их немало. И сайт будет всё проще и проще взломать с каждым новым днём.

    Возможно, ваш хостер имеет возможность внедрять патчи расширенной поддержки от Zend, которые так же закончатся уже в 2026 году.

    Поэтому, обновить стоит. Но перед этим надо убедиться в работоспособности сайта с этой версией.

    1. Полный бэкап файлов и базы. Это даже не обсуждается. Если у хостера бэкап не купили, купите.

    2. Обновите всё, что сможете до последних версий. Разработчики часто добавляют совместимость для новых версий PHP в эти последние версии. Это касается и ядра WordPress, и плагинов, и темы, если она была куплена, а не писалась с нуля. Если тему писали с нуля, то можно связаться с разработчиком, чтобы он дал вам точный ответ, будет ли тема работать, или сделал бы поддержку 8.3

    3. После обновления плагинов и тем проверьте в их документации совместимость этих обновленных версий с PHP 8.3.

    4. Используйте плагин проверки совместимости, чтобы узнать, будет ли ваш сайт работать на 8-х версиях PHP. Он старый, но именно с этой задачей поможет справиться. https://wordpress.org/plugins/php-compatibility-ch...

    5. Самым идеальным вариантом проверки всего этого дела было бы купить у хостера на месяц такой же хостинг и там опробовать сайт на новой версии. Это закроет все галочки в вашем списке сомнений. Если нет возможности этого сделать, то хотя бы на локальной машине запустите сайт на новом PHP. Можно ещё и дебаг-режим включить, чтобы иметь полный расклад в логах.

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

    7. Далее вам нужно наблюдать за работоспособностью и стараться периодически обновлять ядро и плагины, чтобы следующий апгрейд прошел более гладко. Если вы всё-таки решили купить второй хостинг на 5 шаге, то вы можете его даже и оставить как раз для целей проверки обновлений и каких-то новых фич. Не забудьте его тогда закрыть от роботов на всякий случай, чтобы предотвратить индексирование поисковиками.
    Ответ написан
    1 комментарий
  • Macbook air M4 24/32GB 512/1TB или Macbook Pro M4 24GB 512/1TB?

    Экран на Pro просто великолепен. Тут без вопросов. Насчёт звука не знаю, но, возможно, чуточку лучше. Вентилятор Pro вы услышите только при очень большой нагрузке.

    Насчёт диска можно даже не париться. Его всегда можно увеличить путём подключения внешнего. Единственный совет - обратить внимание на то, сколько банок SSD в ноутбуке. По-моему, в M4 они везде ставят уже 2 банки, и не будет просадок по скорости как с одной.

    Главный ресурс ноутбуков Apple - это память. 16 - это уже сильно впритык, и дальше будет только хуже. 24 - это минимум, а 32 гораздо лучше. Учтите, что любые эксперименты с локальным AI - это исключительно Pro с большим объемом памяти. Оно грузит проц страшно. Тут вентилятор будет в помощь. Однако, если такой сильной нагрузки не будет, то у нас появилось светлое пятно на горизонте - это новый эпловский типа-докер, который жрёт в разы меньше памяти, чем оригинал и его аналоги. Таким образом, можно уже более снисходительно смотреть на 24 ГБ.

    В новых Air уже можно подключать 2 внешних монитора. Современные мониторы очень часто можно подключить прямо через USB-c. Как мне кажется, HDMI в прошку воткнули только из-за того, что на конференциях так проще к проектору подключаться.

    Лично я сейчас смело бы взял Air на 24. Он дешевле, экран меня не интересует, я на телике качественный контент смотрю, и мой про-экран не показывает себя на все 100%. А вот веса хочется полегче... Но это моё личное мнение.
    Ответ написан
    Комментировать
  • Не работает сайт crates.io, как исправить вез VPN?

    Половинчатое решение, которое, тем не менее, снимет у вас массу головной боли:

    cargo vendor

    И закоммитить всё в репу. И тогда лезть за зависимостями в эти ваши интернеты вам придётся только на этапе добавления новой зависимости в проект. Зато во всех пайплайнах всё будет работать норм
    Ответ написан
    Комментировать
  • Как сохранить данные на сайте html+css в файл txt?

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

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

    - Formspree или Formcarry Тут еще проще, но больше 50 заполнений в месяц платно.

    - Ну а можно сильно заморочиться и сделать заполнение гугл-таблиц в гугл-докс результатами своей формы. Но тут надо написать обработчик формы на их языке Google Apps Script, опубликовать у них это в виде веб-приложения, и настроить свою форму на адрес этого веб-приложения. Данные будут сохраняться в ту гугл-таблицу, которую вы выбрали в этом скрипте.
    Ответ написан
    Комментировать
  • Можно ли настроить запросы к php через вебсокет и http одновременно?

    3. Вебсокет и unix сокет - это совершенно разные вещи. Websockets - это протокол обмена информацией по TCP/IP.
    Как работают websockets:
    - Клиент начинает с простого HTTP запроса с просьбой апгрейднуться до websockets.
    - Сервер соглашается, и тогда между Клиентом и Сервером устанавливается постоянное TCP/IP соединение, по которому данные могут ходить туда-обратно по протоколу websockets уже без кучи церемониальных ритуалов, как принято в обычном HTTP.

    1. Изначально PHP совершенно не подходил для постоянного соединения, потому что он создавался как скриптовый язык. Скрипт должен был отработать один раз и почистить все следы своего выполнения. Поэтому создатели особо не заботились об утечках памяти и т.д. В результате было очень обременительно создавать постоянно-живущие процессы, необходимые для websockets. Но в настоящее время PHP очень сильно развивается, разработчики уделяют очень много внимания такому сценарию использования языка. PHP стал производительнее и гораздо надёжнее работает с памятью. Однако, напрямую реализовывать долгоживущий сервис самому всё еще муторно, поэтому лучше всего использовать отличные сторонние фреймворки/библиотеки.

    2. Что можно использовать:
    - Ratchet
    - Swoole / OpenSwoole
    - Workerman

    У каждой из этих асинхронных библиотек/фреймворков есть свои особенности и нюансы. Но это всё очень хорошо описано в их документации, так что просто следуйте тому, что там написано, и не волнуйтесь. Советовать что-то одно не буду, потому что на вкус и цвет все фломастеры разные, и выберете то, что будет отвечать вашим конкретным задачам.
    Ответ написан
  • Может ли браузер дублировать POST запрос?

    Такое обычно происходит, когда запрос кидается в lifecycle-хуках, которые могут сработать много раз (например, updated), либо в watch. И когда у вас что-то ошибочно повторно перерендерится, то может бахнуть второй запрос. Дабл-клик по кнопке, которую вы не выключаете сразу после первого клика также не исключается.

    Насчёт идентификатора вашего ничего не могу сказать, потому что не вижу, где и как вы его генерируете.

    Дебажить, дебажить, и ещё раз дебажить VUE.

    1. Если есть возможность запустить фронтенд на локалке в development environment, установите в браузер плагин Vuejs devtools, и посмотрите поведение компонента, кидающего запросы, может быть там что-то увидите.

    2. Откройте devtools браузера и на вкладке Network и кликните ссылку в колонке Initiator (не знаю, как по-русски, не пользуюсь русским в браузере) у этих повторяющихся запросов. Если будут показаны разные участки кода, значит, где-то еще в коде затерялся такой же запрос.

    3. Самое простое: прямо перед строчкой с вызовом запроса axios (прямо перед запросом, в этой же функции, не где-то вне её, а прямо в предыдущей строчке) напишите банальный console.log("Gotcha!!!!"). Если сообщение в консоли браузера появится дважды, значит, проблема исключительно в логике вашей программы.

    4. Если ваш ID действительно генерируется прямо рядом с вызовом запроса, прямо в той же самой функции (что исключает баг с тем, что в запрос подставляется где-то сохранённый и кэшированный фреймворком ID), и это действительно подлый Chromium повторяет запросы из-за крайне нестабильного коннекта у пользователя, то тогда генерируйте ID не просто рандомом, который всё же может повториться, а сгенерируйте нормальный UUID, вероятность повторения которого ЗНАЧИТЕЛЬНО ниже. При приходе запроса сохраняете этот UUID на короткое время где вам удобнее, и если придёт такой же запрос с таким же UUID, то не обрабатываете его. Этот же UUID вам может помочь и в других аспектах: например, вы можете его использовать как "Correlation ID" данного конкретного запроса. Его можно отражать в логах, передавать в другие сервисы, если у вас их несколько. И тогда вы сможете без проблем отслеживать жизненный цикл каждого конкретного запроса.
    Ответ написан
    6 комментариев
  • Кто должен устанавливать cookie, клиент или сервер?

    1. Клиент - ваш враг! Это единственно-верный подход при разработке web-сервисов. У клиента может быть дырявый древний браузер насквозь пробитый вирусами и вредоносными расширениями. Ваша задача - максимально обезопасить и сервер и бедного юзверя от этой гадости.

    2. Поэтому лучше всего генерировать куку на сервере. И не простую, а минимум:
    • HttpOnly
    • Secure
    • SameSite=lax


    3. Приложите уникальный CSRF-токен, и спите относительно спокойно.

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

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

    Серверное решение без JS более надёжно:

    Реализуем паттерн Post/Redirect/Get (PRG) плюс одноразовый уникальный токен.

    - Допустим у нас форма по адресу "/form".
    <form id="myForm" action="/save-data" method="post">
      <!-- ...поля... -->
      <button type="submit">Отправить</button>
    </form>


    - Когда сервер принимает POST запрос по адресу, по которому была отправлена форма "/save-data", он проверяет валидность данных, и ежели всё верно, то не рисует ответ с успешным успехом прямо по этому адресу, а делает серверный редирект 303 на страницу "/success". Т.е. другой адрес. Почему 303? Потому что такой редирект даёт браузеру понять, что на страницу "/save-data" нет смысла возвращаться и хранить его в истории, ведь 303 нам сказал искать контент по другому адресу.
    - Это решает проблему с клавишей "F5" (обновление страницы). К тому же в истории у нас всё нормально без никакого редиректа.
    - Однако кнопка назад может заставить браузер вернуться на страницу с заполненной формой "/form". И повторная отправка сработает.

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

    - При загрузке страницы с формой генерируем этот токен на сервере.
    - Сохраняем токен в сессию
    - Рисуем в HTML формы дополнительное невидимое поле, содержащее этот же токен.

    <form id="myForm" action="/save-data" method="post">
      
      <input type="hidden" name="form_token" value="уникальное_значение_12345">
      
      <!-- ...другие поля... -->
      <button type="submit">Отправить</button>
    </form>


    Когда пользователь нажимает кнопку Отправить правильно, без возвратов назад, происходит следующее:

    1. Сервер получает POST запрос с данными формы на "/save-data"
    2. Забираем значение form_token и сравниваем его с сохранённым ранее токеном из сессии.
    3. Если токены совпадают, то немедленно удаляем его из сессии. Это и есть решение.
    4. Делаем редирект на страницу "/success"
    5. Пользователь видит красивую зелёную галочку и сообщение об успешном успехе. Он доволен.

    Если же он вернётся-таки на страницу формы через кнопку Назад, увидит там всё ту же заполненную форму с данными, и подумает, что он не отправил данные, и попробует снова отправить форму, то произойдёт следующее:

    1. Сервер получает POST запрос с данными формы на "/save-data"
    2. Забираем значение form_token и сравниваем его с сохранённым ранее токеном из сессии.
    3. Т.к. токены не совпадают (в сессии токена нет, мы его удалили ранее), то делаем редирект на страницу "/error", где сообщаем пользователю, что он уже отправлял эту форму раньше, и всё в порядке, пусть не переживает.
    4. Пользователь видит сообщение и утирает пот со лба. Он доволен.

    Решение только выглядит сложным. На самом деле оно простейшее.

    Очистку данных формы конечно можно делать, очищая поля формы с помощью Javascript. Но... Зачем?
    Но если надо, то обрабатывайте событие 'pageshow'. Проверяйте, ежели браузер действительно загрузил страницу из bfcache, и очищаете форму. Можно еще и кнопку снова активировать, если задизаблили её раньше.

    window.addEventListener('pageshow', function(event) {
      // event.persisted бывает true, когда страница загружается из bfcache
      if (event.persisted) {
        const form = document.getElementById('myForm');
        if (form) {
          form.reset(); // Сбрасываем все поля формы
          const button = form.querySelector('button[type="submit"]');
          button.disabled = false; // Убедимся, что кнопка снова активна
          console.log('Страница восстановлена из кэша. Форма сброшена.');
        }
      }
    });
    Ответ написан
    1 комментарий
  • Symfony 6.4 php 8.4 много сыплется deprecated, есть ли пути решения?

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

    Это называется PHP. Потому как PHP изначально создавался как сам себе шаблонизатор HTML. Именно поэтому в коде присутствуют открывающие и закрывающие конструкции "<?php" и "?>"

    Люди, разрабатывавшие PHP, прекрасно понимали, как отвратительно будет выглядеть и читаться код, если они заставят людей писать так, как на вашем скрине. Именно поэтому они придумали совершенно другой синтаксис для условий и циклов, который как раз хорошо подходит для написания шаблонов. Ваш пример можно было бы переписать вот так:

    <?php if ($isSent): ?>
    
        <p>Email sent successfully!</p>
    
    <?php else: ?>
    
    <form method="post" action="<?php echo $_SERVER["PHP_SELF"]; ?>">
        <label for="subject">Тема письма : </label> <br>
        <input type="text" name="subject" size="30"> <br>
    
        <label for="elvismail">Содержание письма : </label> <br>
        <textarea name="elvismail" id="" cols="30" rows="10"></textarea> <br>
    
        <input type="submit" name="submit">
    </form>
    
    <?php endif; ?>


    Погуглите "php endif endfor"
    https://www.php.net/manual/en/control-structures.a...
    Ответ написан
    4 комментария
  • Почему PHP считает int числа после математических операций как float?

    Все коллеги, ответившие выше, правы. float заразен. Всё, чего он касается в математических выражениях, превращается во float.

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

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

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

    Попробуем использовать хотя бы то, что нам дали. Для этого будем придерживаться простого правила:

    все вычисления всегда выносить в отдельные функции, а эти функции всегда типизировать по-максимуму.

    Видим деление, умножение и т.д. - выносим это выражение в функцию. Это, конечно, не идеальное решение, но оно избавит нас хотя бы от какого-то количества проблем. Если в вашем случае вынести код в функцию, то результат не только будет целым числом, но PHP ещё и сообщение DEPRECATED нам выдаст.

    <?php
    
    $sum = 400;
    
    // Было
    $all = $sum * (55 / 100);
    var_dump($all); // На выходе float(220.00000000000003)
    
    // Стало
    $all2 = calculateAll($sum);
    var_dump($all2); // На выходе int(220)
    
    function calculateAll(int $sum): int
    {
        return $sum * (55 / 100);
    }


    Вывод
    float(220.00000000000003)
    PHP Deprecated:  Implicit conversion from float 220.00000000000003 to int loses precision in /Users/vitiok78/Downloads/float.php on line 12
    
    Deprecated: Implicit conversion from float 220.00000000000003 to int loses precision in /Users/vitiok78/Downloads/float.php on line 12
    int(220)
     
    Ответ написан
  • Как отследить кнопку "назад" на телефонах?

    Опишу только принцип, реализацию уже сделаете сами, либо попросите AI.

    Кнопка "назад" работает просто: переводит браузер на предыдущий урл.

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

    https://example.com/page // окна нет
    https://example.com/page#modal1 // окно открыто


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

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

    Решение уважаемого historydev полностью работоспособно, к нему претензий нет никаких, но можно предложить и альтернативу при помощи cloneNode(true) (глубокое клонирование объекта DOM).
    таким образом переменная html у нас остаётся объектом DOM, и её можно далее использовать по коду.
    - var html = e.target.parentNode.querySelector('.firmware-window');
    + var html = e.target.parentNode.querySelector('.firmware-window').cloneNode(true);
    			$('.modal-wrapper').attr('class', 'modal-wrapper').html(html);
    			html.classList.remove('hiden');


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