Статьи

Отображение ошибок из хука save_post в WordPress

Несмотря на существование WP_Error и всех необходимых WP_Error , не существует единого «пути WordPress» для обработки и отображения ошибок. Вы можете указать, что произошла ошибка в функции или методе, возвращая объект WP_Error (и это был мой обычный МО в моей работе), но как только мы это сделаем, как мы будем обрабатывать эти объекты ошибок?

Ошибки WordPress

С AJAX все просто: используйте wp_send_json_{error/success} и обработайте ответ соответствующим образом. Однако общая область, где сгенерированные ошибки должны отображаться пользователю, находится на save_post , который на самом деле немного сложнее, чем кажется.

Причина этого кроется в способе сохранения WordPress. Если вы находитесь на странице типа post.php?post=1234 и вы вносите свои изменения и post.php сохранить , WordPress POST представляет информацию для post.php . После save_post ловушки save_post все, что сделано, сделано, затем оно перенаправляется обратно на страницу редактора post.php?post=1234 .

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

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

Базовая модель

Прежде чем мы начнем, я буду использовать admin_notices для отображения ошибок, которая выглядит следующим образом:

 add_action( 'admin_notices' , 'my_error_message' ) ; 

Этот хук дает нам возможность отображать сообщения над заголовком страницы. Я подробно my_error_message функцию my_error_message для каждого метода. Я также собираюсь игнорировать все, что вы решите сделать в save_post и просто предположить, что в конце хука вы получите $error , которая либо false если ошибок не было, либо объект WP_Error если он был. Поток выполнения будет выглядеть примерно так:

 add_action( 'save_post' , 'my_save_post_function' ); function my_save_function ( $post_id ) { $error = false ; // Do stuff. if ( $something_went_wrong ) { $error = new WP_Error( $code , $msg ); } if ( $error ) { // Handle error. } return true ; } 

Что-то происходит с $post_id (не изображен), и если что-то пойдет не так, мы создадим объект WP_Error который мы обработаем.

Сохраните его в $_SESSION

PHP (и WordPress, собственно) славится своими глобальными переменными. Подобно тому, как переменные $_GET и $_POST содержат данные для этих HTTP-глаголов, глобальная переменная $_SESSION содержит данные для текущего сеанса. Данные $_SESSION сохраняются в сконфигурированном хранилище сеанса (например, memcache, Redis, файловая система) и связываются с сеансом пользователя через cookie. Мы можем использовать это для сохранения и получения нашего сообщения об ошибке.

Как это устроено

Во-первых, вам нужно начать сеанс, так как WordPress не использует сеансы самостоятельно:

 if ( ! session_id() ) { session_start() ; } 

Это должно быть подключено в начале жизненного цикла WordPress, хотя я думаю, что вы могли бы начать сеанс в самом save_post , если хотите. Затем мы сохраняем ошибку в сеансе:

 if ( $error ) { $_SESSION [ 'my_plugin_errors' ] = $error ->get_error_message(); } 

Наконец, в admin_notices :

 if ( array_key_exists( 'my_plugin_errors', $_SESSION ) ) {?> < div class = "error" > < p > <?php echo $_SESSION [ 'my_plugin_errors' ]; ?> </ p > </ div > <?php unset ( $_SESSION [ 'my_plugin_errors' ] ); } 

мы извлекаем сообщение об ошибке из сеанса и выводим его над заголовком страницы.

Плюсы и минусы

С другой стороны, это решение довольно просто реализовать и не требует попадания в базу данных, как это делают два других решения. Основная проблема с этим: WordPress не использует сессии . Он спроектирован так, чтобы быть «не имеющим состояния», поэтому он не предназначен для хранения информации во время выполнения своих операций, что вызовет проблемы у некоторых пользователей, если у них нет хранилища сеансов. По сути, это не «Путь WordPress» и противоречит философии проекта, но это простое и легкое решение проблемы, если вы создаете инструмент для себя или клиента, где у вас есть полный контроль над окружающая обстановка.

Сохранение в переходном процессе

Переходные процессы — это API для кеширования WordPress. Это позволяет вам сохранять любую информацию с датой истечения срока действия, как в любом другом кэше. Внутренний API Transient попытается сохранить ваши данные в кеше объектов, если у вас включен постоянный кеш . Если вы этого не сделаете, он сохранит ваши данные в таблицу wp_options . Таким образом, независимо от того, как настроена серверная часть сайта, есть (несколько) надежный API для сохранения краткосрочных данных. Это отвлекает вас от тяжелой работы.

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

Как это устроено

Нам не нужно ничего начинать, чтобы начать работу с переходными процессами. Когда вы получаете сообщение об ошибке, просто установите переходный процесс с информацией (вам нужно заранее получить $user_id ):

 if ( $error ) { set _transient( "my_save_post_errors_{ $post_id }_{ $user_id }" , $error , 45 ); } 

Вот ссылка на эту функцию . Просто отметьте, что последний параметр — это количество секунд, в течение которых будет длиться переходный процесс. 45 секунд должно быть достаточно времени, чтобы сохранить и получить данные, и если это не так, что-то пошло не так.

Затем в admin_notices :

 if ( $error = get_transient( "my_save_post_errors_{$post_id}_{$user_id}" ) ) { ?> < div class = "error" > < p > <?php echo $error ->get_error_message(); ?> </ p > </ div > <?php delete_transient( "my_save_post_errors_{$post_id}_{$user_id}" ); } 

мы берем WP_Error из кеша и отображаем его сообщение. Не забывайте убирать за собой!

Плюсы и минусы

С другой стороны, это удобный для WordPress способ управления этими ошибками, предназначенный для кеширования подобных вещей. Данные будут очищены сами по себе, даже если вы забудете их очистить самостоятельно, так что это обеспечивает своего рода проверку работоспособности, стирая или игнорируя устаревшие сообщения об ошибках. Основная проблема этого решения заключается в том, что оно должно попасть в базу данных, если у вас не установлен кэш объектов, поэтому, если вы выполняете много работы в save_post и не хотите добавлять другой запрос, это может не будет идеальным решением.

Я также упомяну, что вы можете использовать эту же идею с post_meta . Если я правильно помню, это может не добавить дополнительный запрос, так как он просто добавляет утверждение к запросу для сообщения, но я не уверен. Использование post_meta для временных данных на самом деле не является идеальным назначением, но если оно даст вам повышение производительности, оно может стоить того. Определенно убедитесь, что post_meta за собой, так как это не будет сделано для вас с post_meta как это делают transients .

Добавление GET в редирект

Этот третий метод — фактически способ, которым WordPress показывает свое сообщение «Обновление сообщения»: с параметром $_GET . В экземпляре WordPress он устанавливает значение message в число и использует это число для отображения конкретного сообщения. Вы можете сделать нечто подобное, добавив код WP_Error вместо его полного сообщения к URL-адресу в качестве переменной запроса.

В конце нашего хука save_post мы добавляем код ошибки в параметр URL:

 if ( $error ) { add_filter( 'redirect_post_location' , function ( $location ) use ( $error ) { return add_query_arg( 'my-plugin-error' , $error ->get_error_code(), $location ); }); } 

Анонимные функции НЕ ДОБАВЛЯЛИСЬ до PHP 5.3, а WordPress поддерживает 5.2+, поэтому их использование здесь может лишить вас возможности использовать этот метод, но для них это довольно идеальный вариант использования: извлечение переменной из ее контекста и использование ее в фильтр.

Затем мы отображаем сообщение об ошибке, основываясь на его коде, в хуке admin_notices:

 if ( array_key_exists( 'my-plugin-error' , $_GET ) ) { ?> <div class= "error" > <p> <?php switch( $_GET [ 'my-plugin-error' ]) { case 'an_error_code' : echo 'The post failed to save because problems.' ; break ; case 'another_error_code' : echo 'The post failed to save because reasons.' ; break ; default: echo 'An error ocurred when saving the post.' ; break ; } ?> </p> </div><?php } 

Плюсы и минусы

Большой плюс в том, что вам никогда не придется обращаться к базе данных. Все коды ошибок хранятся и извлекаются в памяти, что делает его намного более производительным, чем другие. Отрицательная сторона — вы должны дублировать сообщения как в экземпляре объекта WP_Error и в операторе switch. Вы не можете использовать константу или набор переменных или что-то подобное, потому что тогда строки не могут быть переведены. Если у вас ограниченное количество кодов ошибок, это не должно быть проблемой, но если ваше приложение становится большим, поддерживать его в долгосрочной перспективе может быть сложнее.

Вывод

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

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