/** * 2015-02-04 * Обратите внимание, что вряд ли мы вправе кэшировать результат при парметре $store = null, * ведь текущий магазин может меняться. * @param int|string|null|bool|StoreInterface $store [optional] * @return StoreInterface|Store * @throws \Magento\Framework\Exception\NoSuchEntityException|Exception * https://github.com/magento/magento2/issues/2222 */ function df_store($store = null) { /** @var StoreInterface $result */ $result = $store; if (is_null($result)) { /** * 2015-11-04 * По аналогии с @see \Magento\Store\Model\StoreResolver::getCurrentStoreId() * https://github.com/magento/magento2/blob/f578e54e093c31378ca981cfe336f7e651194585/app/code/Magento/Store/Model/StoreResolver.php#L82 */ /** @var string|null $storeCode */ $storeCode = df_request(\Magento\Store\Model\StoreResolver::PARAM_NAME); if (is_null($storeCode)) { $storeCode = df_store_cookie_m()->getStoreCodeFromCookie(); } if (is_null($storeCode)) { $storeCode = df_request('store-view'); } /** * 2015-08-10 * Доработал алгоритм. * Сначала мы смотрим, не находимся ли мы в административной части, * и нельзя ли при этом узнать текущий магазин из веб-адреса. * По аналогии с @see Mage_Adminhtml_Block_Catalog_Product_Grid::_getStore() * * 2015-09-20 * При единственном магазине * вызываемый ниже метод метод @uses \Df\Core\State::getStoreProcessed() * возвратит витрину default, однако при нахождении в административной части * нам нужно вернуть витрину «admin». * Например, это нужно, чтобы правильно работала функция @used-by df_is_backend() * Переменная $coreCurrentStore в данной точке содержит витрину «admin». * * 2015-11-04 * При нахождении в административном интерфейсе * и при отсутствии в веб-адресе идентификатора магазина * этот метод вернёт витрину по-умолчанию, а не витрину «admin». * * Не знаю, правильно ли это, то так делает этот метод в Российской сборке для Magento 1.x, * поэтому решил пока не менять поведение. * * В Magento 2 же стандартный метод \Magento\Store\Model\StoreManager::getStore() * при вызове без параметров возвращает именно витрину по умолчанию, а не витрину «admin»: * https://github.com/magento/magento2/issues/2254 * «The call for \Magento\Store\Model\StoreManager::getStore() without parameters * inside the backend returns the default frontend store, not the «admin» store, * which is inconsistent with Magento 1.x behaviour and I think it will lead to developer mistakes.» */ if (is_null($storeCode) && df_is_backend()) { $storeCode = df_request('store', 'admin'); } if (!is_null($storeCode)) { $result = df_store_m()->getStore($storeCode); } } return is_object($result) ? $result : df_store_m()->getStore($result); }
/** * 2015-12-14 * Добавил возможность передачи в качестве первого параметра @see O * причём как в виде объекта, так и строки-класса. * * Такая возможность позволяет нам эффективно рендерить шаблоны без иерархии своих классов-блоков. * В Российской сборке для Magento 1.x * нам приходилось дублировать один и тот же код в классе базовой модели (аналоге класса O), * и в 2-х базовых классах блоков (абстрактном и блоке с шаблоном), т.е. в 3 местах. * Теперь же нам этого делать не нужно. * * @used-by df_phtml() * @param string|O|null $type * @param string|array(string => mixed) $data [optional] * @param string|null $template [optional] * * 2016-11-22 * @param array $vars [optional] * Параметры $vars будут доступны в шаблоне в качестве переменных: * @see \Magento\Framework\View\TemplateEngine\Php::render() extract($dictionary, EXTR_SKIP); * https://github.com/magento/magento2/blob/2.1.2/lib/internal/Magento/Framework/View/TemplateEngine/Php.php#L58 * * @return AbstractBlock|BlockInterface|Template */ function df_block($type, $data = [], $template = null, array $vars = []) { /** * 2015-12-14 * $type может быть как объектом, так и строкой: * https://3v4l.org/udMMH */ /** @var O $context */ if (!is_a($type, O::class, true)) { $context = null; } else { $context = is_object($type) ? $type : new $type(); $type = null; } if (is_null($type)) { $type = df_is_backend() ? BackendTemplate::class : Template::class; } /** @var string|null $template */ if (is_string($data)) { $template = $data; $data = []; } /** @var AbstractBlock|BlockInterface|Template $result */ /** * 2016-11-22 * В отличие от Magento 1.x, в Magento 2 нам нужен синтаксис ['data' => $data]: * @see \Magento\Framework\View\Layout\Generator\Block::createBlock(): * $block->addData(isset($arguments['data']) ? $arguments['data'] : []); * https://github.com/magento/magento2/blob/2.1.2/lib/internal/Magento/Framework/View/Layout/Generator/Block.php#L240 * В Magento 1.x было не так: * https://github.com/OpenMage/magento-mirror/blob/1.9.3.1/app/code/core/Mage/Core/Model/Layout.php#L482-L491 */ $result = df_layout()->createBlock($type, dfa($data, 'name'), ['data' => $data]); // 2016-11-22 $result->assign($vars); if ($template && $result instanceof Template) { $result->setTemplate(df_append($template, '.phtml')); } if ($context) { // 2016-11-22 // «Sets the object that should represent $block in template.» $result->setTemplateContext($context); } return $result; }
/** * 2015-09-27 * Цель метода — получение информации о формировании в данный момент заголовка страницы. * @uses \Magento\Framework\View\Page\Title::get() * @param Sb $sb * @param \Closure $proceed * @return string */ public function aroundGet(Sb $sb, \Closure $proceed) { df_state()->renderingTitle(true); try { $result = $proceed(); /** * Делаем браузерные заголовки административной части * более короткими и понятными: оставляем лишь первую и последнюю части заголовка. */ if (df_is_backend()) { /** @var string[] $resultA */ $resultA = explode(Sb::TITLE_GLUE, $result); $result = 3 > count($resultA) ? $result : implode(Sb::TITLE_GLUE, [df_first($resultA), df_last($resultA)]); } } finally { df_state()->renderingTitle(false); } return $result; }
/** * 2015-11-19 * https://mage2.pro/t/228 * «Propose to add a fieldset-specific element renderer» * @override * @param string $elementId * @param string $type * @param array $config * @param bool|false $after * @param bool|false $isAdvanced * @return AE */ public function addField($elementId, $type, $config, $after = false, $isAdvanced = false) { /** @var AE $result */ $result = parent::addField($elementId, $type, $config, $after, $isAdvanced); /** @var RendererInterface|null $renderer */ $renderer = $this->getElementRendererDf(); if (!$renderer && df_is_backend()) { /** * 2015-11-22 * По аналогии с https://github.com/magento/magento2/blob/2.0.0/app/code/Magento/Backend/Block/Widget/Form.php#L70-L75 * https://mage2.pro/t/239 * @uses \Magento\Backend\Block\Widget\Form\Renderer\Fieldset\Element */ $renderer = \Df\Backend\Block\Widget\Form\Renderer\Fieldset\Element::s(); } if ($renderer) { $result->setRenderer($renderer); } return $result; }
/** * 2016-02-15 * @override * How is a payment method's isAvailable() used? https://mage2.pro/t/721 * * @see \Magento\Payment\Model\MethodInterface::isAvailable() * https://github.com/magento/magento2/blob/6ce74b2/app/code/Magento/Payment/Model/MethodInterface.php#L343-L350 * @see \Magento\Payment\Model\Method\AbstractMethod::isAvailable() * https://github.com/magento/magento2/blob/6ce74b2/app/code/Magento/Payment/Model/Method/AbstractMethod.php#L805-L825 * * @param CartInterface|Q $quote [optional] * @return bool */ public function isAvailable(CartInterface $quote = null) { /** @var bool $result */ $result = ($this->availableInBackend() || !df_is_backend()) && $this->isActive($quote ? $quote->getStoreId() : null); if ($result) { /** @var DataObject $checkResult */ $checkResult = new DataObject(['is_available' => true]); df_dispatch('payment_method_is_active', ['result' => $checkResult, 'method_instance' => $this, 'quote' => $quote]); $result = $checkResult['is_available']; } if ($result && $quote) { /** @var float $amount */ $amount = $this->s()->cFromBase($quote->getBaseGrandTotal(), $quote); /** @var int|float $min */ /** @var int|float $max */ list($min, $max) = dfa($this->amountLimits(), $this->s()->currencyC($quote), [null, null]); $result = (is_null($min) || $amount >= $min) && (is_null($max) || $amount <= $max); } return $result; }
/** * 2016-08-17 * Цель плагина — форматирование внешнего вида платёжного адреса в том случае, * когда отключенен запрос этого адреса у покупателей, и данные, вероятно, пусты. * * Раньше я пробовал реализовать эту функциональность обработкой события «customer_address_format»: * https://github.com/mage2pro/core/blob/1.6.16/Customer/Observer/AddressFormat.php?ts=4#L37-L92 * Однако такой подход оказался невозможен, * потому что метод @see \Magento\Sales\Model\Order\Address\Renderer::format() выглядит так: * $formatType = $this->addressConfig->getFormatByCode($type); if (!$formatType || !$formatType->getRenderer()) { return null; } $this->eventManager->dispatch('customer_address_format', [ 'type' => $formatType, 'address' => $address ]); return $formatType->getRenderer()->renderArray($address->getData()); * * Во-первых, надо обратить внимание, что $formatType — это одиночка. * 1) Сначала я наивно пытался её модифицировать, но тогда, раз это одиночка, * то мои изменения применялись ко всем последующим адресам, * в том числе к непустым адресам и адресам доставки. * 2) Второй попыткой было в обработчике события подменять одиночку $formatType на свой объект. * Но ведь метод @see \Magento\Sales\Model\Order\Address\Renderer::format() игнорирует * результат обработчика события, и продолжает использовать одиночку, * так что в таком подходе толку нет. * * Поэтому пришлось делать этот плагин. * * @see \Magento\Sales\Model\Order\Address\Renderer::format() * @param Sb $sb * @param \Closure $proceed * @param Address $a * @param string $type * @return string */ public function aroundFormat(Sb $sb, \Closure $proceed, Address $a, $type) { /** @var string $result */ // 2016-08-17 // Убеждаемся, что firstname и lastname равны null, // чтобы не ломать отображение адресов, для которых информация присутствует // (например, эти адреса могли быть собраны до отключения опции askForBillingAddress). if (df_address_is_billing($a) && !$a->getFirstname() && !$a->getLastname()) { /** @var OP|null $payment */ $payment = $a->getOrder()->getPayment(); if ($payment && dfp_is_my($payment)) { /** * 2016-08-17 * Раньше тут было ещё условие !$method->s()->askForBillingAddress(), * но на самом деле оно ошибочно, * потому что если администратор сначала отключил опцию askForBillingAddress, * собраз заказы, а потом снова включил эту опцию, * то адреса заказов, собранных во время отключения опции, * должны обрабатываться корректно. */ /** * 2016-08-17 * Дальнейший код идёт по аналалогии с кодом * @see \Magento\Sales\Model\Order\Address\Renderer::format() */ /** * 2016-07-27 * По аналогии с @see \Magento\Sales\Model\Order\Address\Renderer::format() * https://github.com/magento/magento2/blob/2.0.0/app/code/Magento/Sales/Model/Order/Address/Renderer.php#L51 * @var DataObject $typeO */ $typeO = $this->addressConfig()->getFormatByCode($type); /** * 2016-07-27 * Если в будущем мы захотим написать что-либо более объёмное, * то можно поставить ещё 'escape_html' => false */ $typeO->addData(['default_format' => __(!df_is_backend() ? 'Not used.' : 'The customer was not asked for it.')]); /** @var RendererInterface|DefaultRenderer|null $renderer */ /** @noinspection PhpUndefinedCallbackInspection */ $renderer = call_user_func([$typeO, 'getRenderer']); if (!$renderer) { $result = null; } else { df_dispatch('customer_address_format', ['type' => $typeO, 'address' => $a]); $result = $renderer->renderArray($a->getData()); } } } return isset($result) ? $result : $proceed($a, $type); }
/** * 2016-08-20 * @return bool */ protected function isFrontend() { return !df_is_backend(); }