Бдительный order php. Как работать с PDO? Полное руководство

Страница оформления плагина woocommerce имеет определенную структуру и функционал. Но данная структура не совсем удобна. Доработаем страницу оформления, разбив на логические блоки: Заказ, Доставка, Форма оплаты, Информация о заказчике и т.д.

Оформление заказа (базовая структура)

Шаблон form-checkout.php
form name=»checkout»

woocommerce_checkout_billing (информация о заказчике)
woocommerce_checkout_shipping (информация о доставке)

h3 Заказ /h3
div id=»order_review» Order-review /div

Шаблон review-order.php (Order-review)
table
позиции товара к покупке
итого
shipping (cart-shipping.php, ajax зависимость от введенной в woocommerce_checkout_billing информации, подробнее про доставку)
/table

div id=»payment» Форма оплаты /div (подключена хуком)

Кнопка Оформить заказ

Подготовил более наглядную схему за какую область оформления отвечает какой шаблон Woocommerce

Блок «Выбор способа доставки»

Шаблон который отвечает за вывод блока с выбором вариантов доставки находиться (почему-то) здесь: cart/cart-shipping.php .

Надпись Доставка, которая изначально там присутствует выводиться этой строкой:

Echo wp_kses_post($package_name);

Добавить заголовок «Выберите способ доставки» и общий вес заказа непосредственно в блок с доставкой, можно хуком

Function action_woocommerce_review_order_before_shipping() { echo "Выбор способа доставки"; global $woocommerce; echo "Общий вес: "; $total_weight = $woocommerce->cart->cart_contents_weight; $total_weight .= " ".get_option("woocommerce_weight_unit"); echo $total_weight; echo ""; }; add_action("woocommerce_review_order_before_shipping", "action_woocommerce_review_order_before_shipping", 10, 0);

Дорабатываем элементы

Если мы не используем функционал расчета доставки и применения купонов — можно отключить ajax обновление. Если отключать ajax не нужно — ниже есть способ без отключения.

// Отключить Ajax на странице оформления jQuery(document.body).on("update_checkout", function(e){ //e.preventDefault(); //e.stopPropagation(); e.stopImmediatePropagation(); //console.log(e); });

Переместить блок Формы оплаты

Remove_action("woocommerce_checkout_order_review", "woocommerce_checkout_payment", 20); add_action("woocommerce_after_order_notes", "woocommerce_checkout_payment", 20); либо подключить к собственному хуку

Отключение расчета доставки

Если доставка довольно сложна и невозможно прописать ее в автоматическом режиме (например: если мы не сможем реально подсчитать какая нужна газель), то мы отключаем расчет доставки: просто оставляем в методах доставки Самовывоз и Доставка (с 0 ценой). При этом из формы информации о заказчике удаляем поля относящиеся доставки и наоборот из формы доставки удаляем поля касающиеся заказчика. И создаем скрипт при нажатии на пункт Доставка раскрывающий форму доставки.

Id поля с пунктом Доставка

$(document).ready(function() { if($(«#shipping_method_0_flat_rate-6 «).is(‘:checked’)) { $(‘.shipping_address’).show(); } $(‘.shipping_method’).click(function(){ if($(«#shipping_method_0_flat_rate-6 «).is(‘:checked’)) { $(‘.shipping_address’).show(); $(‘.shipping_address’).html(‘

Информация о доставке

‘); } else { $(‘.shipping_address’).html(‘

‘); $(‘.shipping_address’).hide(); } }); });

Данный вариант реализации не является полноценным решением, т.к. имеет ряд недостатков, и в своей основе имеет довольно примитивный подход

Взаимосвязь варианта доставки и полей

Метод для 2-х вариантов доставки (для 3-х и более не подойдет)

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

Проблема woocommerce проявляется в том, что форма Детали доставки раскрывается при нажатии заголовка Доставка по другому адресу? (#ship-to-different-address) и вместе с сокрытием поля отключается верификация полей доставки. При двух вариантах доставки (Самовывоз и Доставка) при их переключении можно поставить с помощью jquery событие (click) на id ship-to-different-address. При этом необходимо сделать, чтоб одно из полей изначально было активным, в зависимости от необходимости.

$("#shipping_method_0_flat_rate-2").attr("checked",true); $("input:radio").on("change", function () { $("#ship-to-different-address-checkbox").click(); });

Для того чтобы поля доставки были изначально открытыми необходимо установить настройку доставки в опциях woocommerce
Назначение доставки > По умолчанию для адреса доставки клиента

В поле город можно по-умолчанию сделать автоподставление
$(«#shipping_city»).val(«Ростов-на-Дону»);

Метод для 3-х и более вариантов доставки

Изначально делаем доставку (один из вариантов) и поля доставки активными. Далее jquery-магия

$("input:radio").on("change", function () { if($("#shipping_method_0_local_pickup-2").is(":checked")) { if($("#ship-to-different-address-checkbox").is(":checked")) { $("#ship-to-different-address-checkbox").click(); } else { } } else { if($("#ship-to-different-address-checkbox").is(":checked")) { } else { $("#ship-to-different-address-checkbox").click(); } } });

В этом случае мы проверяем активно ли поле Самовывоз (#shipping_method_0_local_pickup-2) и в зависимости от этого проверяем скрытый (я его скрываю) checkbox (Доставка по другому адресу? #ship-to-different-address).

Без отключения ajax

Ajax на странице оформления заказа так или иначе нужен (для тех же купонов). Поэтому немного изменим (изменения коснулись первой строки, т.к. элементы DOM были еще не созданы) универсальный скрипт отключения доставочных полей. #shipping_method_0_local_pickup-3 — id поля с самовывозом.

$("body").on("change", "input:radio", function(e) { if($("#shipping_method_0_local_pickup-3").is(":checked")) { if($("#ship-to-different-address-checkbox").is(":checked")) { $("#ship-to-different-address-checkbox").click(); } else { } } else { if($("#ship-to-different-address-checkbox").is(":checked")) { } else { $("#ship-to-different-address-checkbox").click(); } } });

И на всякий случаем включаем изначальный выбор на какую-либо доставку

$("#shipping_method_0_flat_rate-4").attr("checked",true);

Напоминание: в настройках доставки Назначение доставки должно быть выбрано - По умолчанию для адреса доставки клиента

Варианты оформления для физ. лица и юр. лица

Задача сделать 2 варианта оформления заказа для физ. лица и для юр. лица. Создание этого функционала описано в статье

У меня проблемы с параметрами в разделе ORDER BY моего SQL. Он не выдает никаких предупреждений, но ничего не выводит.

$order = "columnName"; $direction = "ASC"; $stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY:order:direction"); $stmt->bindParam(":my_param", $is_live, PDO::PARAM_STR); $stmt->bindParam(":order", $order, PDO::PARAM_STR); $stmt->bindParam(":direction", $direction, PDO::PARAM_STR); $stmt->execute();

The:my_param работает, но не:order или:direction . Является ли это внутренним бегством? Могу ли я вставить его непосредственно в SQL? Вот так:

$order = "columnName"; $direction = "ASC"; $stmt = $db->prepare("SELECT * from table WHERE column = :my_param ORDER BY $order $direction");

Существует PDO::PARAM_COLUMN_NAME константа PDO::PARAM_COLUMN_NAME или какой-либо эквивалент?

Благодаря!

Здесь возникает вопрос, что широко любимые подготовленные заявления – это не серебряная пуля, хе-хе 🙂

Да, вы застряли, вставив его непосредственно в SQL с некоторыми предосторожностями, конечно. Каждый оператор / идентификатор должен быть жестко закодирован в вашем скрипте, например:

$orders=array("name","price","qty"); $key=array_search($_GET["sort"],$orders); $order=$orders[$key]; $query="SELECT * from table WHERE is_live = :is_live ORDER BY $order"; - $orders=array("name","price","qty"); $key=array_search($_GET["sort"],$orders); $order=$orders[$key]; $query="SELECT * from table WHERE is_live = :is_live ORDER BY $order";

То же самое для направления.

Обратите внимание, что bindParam не ускользает, так как не требуется никакого экранирования. он имеет обязательную силу.

Я не думаю, что вы можете:

  • Использовать заполнители в порядке order by
  • Связывать имена столбцов: вы можете связывать только значения – или переменные и вводить их значение в подготовленный оператор.

Возможно использование подготовленных операторов в предложении ORDER BY , к сожалению, вам нужно передать порядок столбца с именем и требуется установить PDO_PARAM_INT с типом.

В MySQL вы можете получить порядок столбцов с этим запросом:

SELECT column_name, ordinal_position FROM information_schema.columns WHERE table_name = "table" and table_schema = "database"

$order = 2; $stmt = $db->prepare("SELECT field from table WHERE column = :param ORDER BY:order DESC"); $stmt->bindParam(":param", $is_live, PDO::PARAM_STR); $stmt->bindParam(":order", $order, PDO::PARAM_INT); $stmt->execute();

Я не думаю, что вы можете получить ASC / DESC как часть подготовленного оператора, но столбец вы можете.

Order by case:order when "colFoo" then colFoo when "colBar" then colBar else colDefault end $direction

Поскольку ASC / DESC – это всего лишь два возможных значения, вы можете легко проверить и выбрать между ними как жестко установленные значения.

Вы также можете использовать функции ELT (FIELD (,) ,) для этого, но тогда упорядочение всегда будет выполняться как строка, даже если это числовой столбец.

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

Создайте запрос с помощью стандартных экранов и выполните его напрямую.

Это возможно . Вы можете использовать число вместо имени поля в предложении "order by". Это число, начинающееся с 1 и имеющее порядок имен полей в запросе. И вы можете конкатенировать строку для ASC или DESC. Например «Выберите col1, col2, col3 из tab1 order на?» + StrDesc + «limit 10,5». strDesc = "ASC" / "DESC".

Если я не ошибаюсь, Паскаль прав.
Единственное связывание, возможное в PDO, – это привязка значений, как вы это делали с параметром ": my_param".
Однако в этом нет вреда:

$stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY ".$order ." ".$direction); $stmt->bindParam(":my_param", $is_live, PDO::PARAM_STR); $stmt->execute();

Единственное, что следует обратить внимание, это правильное преодоление $order и $direction , но поскольку вы устанавливаете их вручную и не устанавливаете их через пользовательский ввод, я думаю, что вы все настроены.

Создайте условие if-else.
Если (ascCondion), то привяжите значения, но жесткий код ORDER BY columnName ASC
еще
Свяжите значения, но жесткий код ORDER BY COlumnName DESC

Когда вы просматриваете километры кода, у вас вполне может возникнуть вопрос: "Почему все сделано так, как сделано?" Лично я особенно замечаю вещи, которые могут и должны быть улучшены, когда дело касается тяжелых запросов в БД.

Разработка без фреймворка

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

$pdo = new \PDO($config["db"]["dsn"], $config["db"]["username"], $config["db"]["password"]); $sql = "SELECT * FROM `gen_contact` ORDER BY `contact_modified` DESC"; $stmt = $pdo->prepare($sql); $stmt->execute(); $data = $stmt->fetchAll(\PDO::FETCH_OBJ); echo "Getting the contacts that changed the last 3 months" . PHP_EOL; foreach ($data as $row) { $dt = new \DateTime("2015-04-01 00:00:00"); if ($dt->format("Y-m-d") . "00:00:00" < $row->contact_modified) { echo sprintf("%s (%s)| modified %s", $row->contact_name, $row->contact_email, $row->contact_modified) . PHP_EOL; } }

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

  • Код не дает возможность повторного использования. Когда будет надо задействовать похожий функционал, вам придется дублировать существующий код.
  • Даже если вы для выборки используете $stmt->fetchAll(\PDO::FETCH_OBJ); у вас остается проблема, т.к. на выходе получится массив объектов. При большой выборке это пожрет море памяти.
  • Фильтрация делается с помощью функции. Это означает, в том случае, если потребуются другие условия фильтрования, вам нужно будет модифицировать текущую логику, что далеко не прибавит удобства для сопровождения и расширения функционала.
Итераторы

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

Итак, давайте представим, что вы продолжаете использовать PDO для получения данных. У нас есть для варианта:

  • Использовать PDOStatement::fetchAll() для получения всех данных за один проход.
  • Использовать PDOSTatement::fetch() для получения одной строки за одну итерацию.

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