Приятный бонус такой тренировки скиллов заключается в знакомстве с новыми технологиями и бизнес процессами, которое кроме всего прочего востребованы в реальных задачах.
На этот раз надо было сделать страничку для формирования заказа покупателя в сервисе «Мой склад». Для меня это как полёт на Луну: в веб разработке я чуть меньше чем новичок, с фронтэндом знаком только по наслышке, а тут целую страницу надо разработать, ох ты Йожик!
Любая критика и советы приветствуются.
В коментах очень много ругательств, моё решение настолько ужасно, что для него сделали рефакторинг во что то приличное:
michael_vostrikov
От нечего делать сделал небольшой рефакторинг этого задания (хотя там много чего еще можно поменять), не столько для вас, сколько для тех, кто потом найдет в поиске эту статью:
коммиты, разметка, отправка формы.
Поехали!
Первым делом конечно гуглить, нагуглилась только ссылка на документацию, туториалов, примеров — ноль.
Ещё нагуглилось: «JSON API доступен для подписчиков на всех тарифах, кроме Бесплатного» уупс! Платного мне конечно ни кто не дал, покупать не камильфо, но я подумал что если дали такое задание, то наверное на Бесплатном что то там функционирует и продолжил работу.
И конечно нагуглилось «moysklad-client — npm — JavaScript клиент для комфортной работы с API сервиса МойСклад», но я с JS исключительно на «Вы», и по условиям задания, написать надо на PHP. Так что даже разбираться не стал, что там на JS можно делать.
Первое
Первое что надо сделать, это познакомиться с документацией. Познакомился.
Второе — составить план. Составил.
План, начало.
Действие первое — авторизация.
Действие второе — показать список Номенклатур.
Действие третье — добавить Заказ покупателя.
Действие четвёртое — добавить Позиции в Заказ покупателя.
Цель достигнута, конец плана.
Авторизация
Я видел код в котором для общения с API использовался cUrl. Я не знаю что такое cUrl, я не знаю как принято общаться с API, но если есть код который можно скопипастить, то проверить его пригодность не сложно. Скопировал вставил, обработал напильником — получилось.
Не буду утомлять вас интимными подробностями о дружбе напильника с копипастой, вот работающий код:
function setupCurl($apiSettings)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
$userName = $apiSettings[MOYSKLAD_USERNAME];
$userPassword = $apiSettings[MOYSKLAD_PASSWORD];
curl_setopt($curl, CURLOPT_USERPWD, "$userName:$userPassword");
curl_setopt($curl, CURLOPT_USERAGENT, $apiSettings[MOYSKLAD_USER_AGENT]);
return $curl;
}
Параметры curl:
- RETURNTRANSFER — не только отправляем запрос, но и записываем ответ;
- USERPWD — реквизиты аутентификации;
Остальные опции не знаю зачем нужны, тупо копипаста.
Не факт, что заголовки авторизации надо отправлять в каждом запросе, хотя не факт, что они не сбросились после первой же отправки… кто подскажет?
Итак, это была инициализация объекта curl для обмена сообщениями с сервером API.
Использование:
function curlExec($curlObject)
{
$response = curl_exec($curlObject);
$curlErrorNumber = curl_errno($curlObject);
if ($curlErrorNumber) {
throw new Exception(curl_error($curlObject));
}
return $response;
}
Чистая копипаста, не спрашивайте меня почему так.
Показать список Номенклатур
Одних номенклатур оказалось мало, для Заказа покупателя, надо указать юридическое лицо Поставщика и контрагента Покупателя. У владельца учётки «Мой склад» может быть несколько юридических лиц, контрагентов — ясно понятно 100500, но конкретный Заказ покупателя, это заказ конкретного Контрагента в адрес конкретного Юридического лица.
Поэтому с номенклатурами обождём, займёмся сторонами «договора» — сделки.
Юридические лица
$curl = setCurl(
$curl,
$apiSettings[MOYSKLAD_API_URL] . $apiSettings[MOYSKLAD_GET_JURIDICAL_PERSON],
$apiSettings[MOYSKLAD_GET_JURIDICAL_PERSON_METHOD]);
$persons = getJuridicalPerson($curl);
function setCurl(&$curlObject, $uri, $method)
{
curl_setopt($curlObject, CURLOPT_URL, $uri);
curl_setopt($curlObject, CURLOPT_HTTPGET, true);
switch ($method) {
case MOYSKLAD_METHOD_GET:
break;
case MOYSKLAD_METHOD_POST:
curl_setopt($curlObject, CURLOPT_POST, true);
break;
case MOYSKLAD_METHOD_PUT:
curl_setopt($curlObject, CURLOPT_PUT, true);
break;
}
return $curlObject;
}
function getJuridicalPerson($curlObject)
{
$response = curlExec($curlObject);
$data = json_decode($response, true);
$result = $data['rows'];
return $result;
}
Извиняюсь за ужасные названия констант, но мне с такими спокойней, точно ни с чем не перепутаю. Да я знаю что у case (switch) есть ветка default, но мне спокойней вбетонировать в код значение по умолчанию и не надеяться на превратности судьбы с case.
У каждой команды API свой адрес и свой метод, setCurl — устанавливает адрес и метод.
Для получения списка юридических лиц устанавливаем соответствующий адрес и метод ( адрес и метод задаются в настройках, настройки подгружаются методом function getSettings(){ $apiConfig = include('moysklad_curl_details.php'); return $apiConfig;} ).
После этого методом getJuridicalPerson исполняем curl, получаем ответ в JSON, из ответа забираем только массив 'rows'. Получили, сохранили, отложили.
С Контрагентами поступаем аналогично: setCurl => getCounterparty, Номенклатуры по тому же алгоритму: setCurl => getNomenclature.
Если бы это было не тестовое на два вечера после работы, а на два дня безработного специалиста, то можно было бы это автоматизировать, но это было тестовое в стиле — «лишь бы работало», поэтому я не стал изгаляться.
Для меня цель тестового была в том что бы пригубить и попробовать на вкус JSON API, рисовать красоту — цели не было.
Данные получили — это вообще не вопрос, дело дурацкое — дело не хитрое, интересней было как то это вывести на страничку, а потом со странички забрать, вот это была задачка.
Фронтэнд
Не знаю как правильно, я сделал так:
echo '<form action="#" onsubmit="return false;" id="orderForm" ><p>Доступные юридические лица:<br />';
foreach ($persons as $key => $person) {
$personId = $person['id'];
echo '<label for="' . $personId . '">' . $person['name'] . '</label><input type="radio" data-organization-type="1" id="' . $personId . '" name="organization"><br />';
}
echo 'Доступные контрагенты:<br />';
foreach ($counterparty as $key => $person) {
$personId = $person['id'];
echo '<label for="' . $personId . '">' . $person['name'] . '</label><input type="radio" data-counterparty-type="1" id="' . $personId . '" name="counterparty"><br />';
}
echo 'Номенклатура товаров:<br />';
foreach ($nomenclature as $key => $position) {
$positionId = $position['id'];
echo '<label for="' . $positionId . '">' . $position['name'] . ', количество для заказа => </label><input type="text" id="' . $positionId . '" data-position-type="1"><br />';
}
echo '
<input type="submit" name="Сформировать заказ покупателя" onclick="sendOrder();"><br /></p></form>'
Общий алгоритм:
- пишем название раздела
- пишем название позиции,
- пишем тег input, в атрибут id пишем идентификатор полученный из API,
- пишем соответствующий атрибут data-organization-type / data-counterparty-type / data-position-type, поскольку для получения атрибута надо присвоить значение, то присваиваем
(вообще конечно можно использовать атрибуты без значений, но я не разобрался как).
По клику на кнопку «Сформировать заказ покупателя», форма не отправляется — «return false;», но вызывается функция — «sendOrder();».
Отправить заказ
echo '
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
function sendOrder(){
var $text_field = $('#orderForm :input:text');
var position = {};
$text_field.each(function() {
var this_val = $(this).val();
var may_assign = this_val>0 || this_val !="";
var is_it_position = $(this).data('position-type');
if ( may_assign && is_it_position > 0){
position[this.id] = this_val;
}
});
var $radio_field = $('#orderForm :input:radio:checked');
var counterparty = {};
var organization = {};
$radio_field.each(function() {
var this_val = $(this).val();
var is_it_counterparty = $(this).data('counterparty-type');
var is_it_organization = $(this).data('organization-type');
var may_assign = this_val>0 || this_val !="";
if ( may_assign && is_it_counterparty > 0){
counterparty[this.id] = this_val;
}
if ( may_assign && is_it_organization > 0){
organization[this.id] = this_val;
}
});
C JS мне кажется всё более чем прозрачно:
- $('#orderForm :input:text'); — выбрали все теги input с типом text внутри тега с идентификатором orderForm
- $text_field.each — для каждого элемента выполняем анонимную функцию
- var this_val = $(this).val(); — сохранили значение
- var may_assign = this_val>0 || this_val !=""; — вычислили что значение не пустое
- var is_it_position = $(this).data('position-type'); — вычислили что атрибут data-position-type установлен
- if ( may_assign && is_it_position > 0){ position[this.id] = this_val; } — если значение не пустое и этот input соответствует позиции, то добавляем в массив позиций соответствующий элемент, идентификатор в качестве индекса гарантирует уникальность.
Проделываем такую же акробатику с юридическими лицами и контр агентами, с тем отличием что для анализа выбираем все input с типом «radio» в состоянии «checked»:
$('#orderForm :input:radio:checked'), и кроме того значение элемента input нам не требуется, нам просто надо знать кого ( одного ) из всего списка выбрал наш Покупатель.
Теперь когда данные для отправки в обработку готовы, надо сформировать запрос:
var postData = JSON.stringify({position : position, counterparty : counterparty , organization : organization});
console.log(postData);
$.ajax({
type: "POST",
url: "moyskald_add_order.php",
data: postData,
contentType: "application/json; charset=utf-8",
dataType: "text",
timeout: 10000,
error: function(){
alert("сбой добавления заказа");
},
success: function(data){alert(data);},
failure: function(errMsg) {
alert(errMsg);
}
});
К этой копипасте мне добавить не чего, метод отправки — «POST», обработка будет выполнена — «moyskald_add_order.php», данные уходят в формате -«application/json; charset=utf-8», приходят в формате — «text».
Данные вообще не нужны, хотя конечно хороший тон это сообщить пользователю «Ваш заказ принят» или «Сбой добавления заказа», и ладно.
Едем дальше, следующий пункт прибытия — «Обработка».
Обработка
С обработкой вышла осечка. Я ламер-эникейщик и для разработки использую XAMPP (под Win10), который как то раз настроил и забыл. И вот что то там такое настроено, что я GET запросы в PHP-скрипт получаю как нормальный человек, а POST-запросы, только через одно место.
Но GET — не камильфо, потому что GET кэшуруется, а у нас не факт что ответ сервера будет точно таким как в прошлый раз, особенно когда ты ведёшь разработку, и серверная логика меняется каждые пять минут.
Поэтому пришлось смириться с использованием чёрной магии в виде file_get_contents(«php://input»), потому что, что бы я ни делал, но var_export($POST) стабильно выдавал «array()». Между прочим, буду благодарен за серию пинков в верном направлении.
А дальше всё просто:
$data = json_decode($rawData, true);
$rawPosition = $data['position'];
$rawCounterparty = $data['counterparty'];
$rawOrganization = $data['organization'];
Разобрали входные данные.
Дёрнули Юридическое лицо и контрагента:
const FIRST_INDEX = 0;
$counterpartyId = $counterpartyIdCollection[FIRST_INDEX];
$organizationId = $organizationIdCollection[FIRST_INDEX];
Сформировали поля запроса:
$textAddCustomerOrder = '
{
"name": "' . time() . '",
"organization": {
"meta": {
"href": "https://online.moysklad.ru/api/remap/1.1/entity/organization/' . $organizationId . '",
"type": "organization",
"mediaType": "application/json"
}
},
"agent": {
"meta": {
"href": "https://online.moysklad.ru/api/remap/1.1/entity/counterparty/' . $counterpartyId . '",
"type": "counterparty",
"mediaType": "application/json"
}
}
}
';
$apiSettings = getSettings();
$curl = setupCurl($apiSettings);
$curl = setCurl(
$curl,
$apiSettings[MOYSKLAD_API_URL] . $apiSettings[MOYSKLAD_ADD_CUSTOMER_ORDER],
$apiSettings[MOYSKLAD_ADD_CUSTOMER_ORDER_METHOD]);
curl_setopt($curl, CURLOPT_POSTFIELDS, $textAddCustomerOrder);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($textAddCustomerOrder))
);
Тут получилось схалявить и зафигачить JSON без json_encode, тупо вклеить нужные идентификатору, в нужные места. Обязательно делаем POSTFIELDS — $textAddCustomerOrder, HTTPHEADER — 'Content-Length: '. strlen($textAddCustomerOrder).
отправляем запрос на обработку на сервер API: $customerOrderId = setCustomerOrder($curl), в ответе забираем 'id'.
Заказ добавлен.
Добавить Позиции в Заказ покупателя
С этим пунктом Плана, ни каких проблем, кроме необходимости использования json_encode для форматирования текста запроса и floatval для количества товара в позиции. Без floatval сервер API выдаёт ошибку формата для поля «quantity» (и «reserve» соответственно).
$isPositionArray = is_array($rawPosition);
$orderPositions= array();
if ($isPositionArray) {
foreach ($rawPosition as $id => $quantity) {
$positionQuantity=floatval($quantity);
$orderPositions[] =
[
"quantity" =>$positionQuantity,
"price"=>0,
"discount"=>0,
"vat"=>0,
"assortment" =>[
"meta"=>[
"href"=>"https://online.moysklad.ru/api/remap/1.1/entity/product/$id",
"type"=>"product",
"mediaType"=>"application/json"
]
],
"reserve"=>$positionQuantity,
];
}
}
Ремарка: foreach ($rawPosition as $id => $quantity), в ключах массива записаны идентификаторы позиций, в значениях элементов массива — количество для заказа.
Мой фэншуй требует создания каждого массива по отдельности и добавления всех «элементарных» массивов в общую кучу, но… мне кажется это будет излишний фанатизм.
Не уверен что в заказе покупателя надо выставлять резерв, но мне кажется это больше бизнес требование, чем механика работы с API.
С ценой смешно вышло, в задании требовалось вывести список номенклатур, для меня это просто перечень наименований и соответствующих ТТХ, в которые цена не входит, но если подумать, то цена это самое первое ТТХ в подобном приложении, но у меня в форме заказа цены нет, поэтому — проехали.
С отправкой API запроса теперь мне кажется всё предельно ясно:
$jsonResponse = 'empty';
$isContainPosition = count($orderPositions)>0;
if($isContainPosition ){
$jsonOrderPositions= json_encode($orderPositions);
$curl = setupCurl($apiSettings);
$curl = setCurl(
$curl,
$apiSettings[MOYSKLAD_API_URL]
. $apiSettings[MOYSKLAD_ADD_ORDER_POSITION_PREFIX]
. $customerOrderId
. $apiSettings[MOYSKLAD_ADD_ORDER_POSITION_SUFFIX],
$apiSettings[MOYSKLAD_ADD_ORDER_POSITION_METHOD]);
curl_setopt($curl, CURLOPT_POSTFIELDS, $jsonOrderPositions);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($jsonOrderPositions))
);
$jsonResponse = setCustomerOrderPosition($curl);
}
Адрес-команда запроса «вычисляется» несколько странным образом:
$apiSettings[MOYSKLAD_API_URL]
. $apiSettings[MOYSKLAD_ADD_ORDER_POSITION_PREFIX]
. $customerOrderId
. $apiSettings[MOYSKLAD_ADD_ORDER_POSITION_SUFFIX]
По документации должно быть :"/entity/customerorder/{id}/positions".
Типа даже нативная PHP строка, бери да прямо так и пиши — {id} подставиться само, но я не могу допустить хардкода в отношении записи команды API, нет конечно за время написания тестового команда не поменяется, но феншуй требует такие глобальные вещи выносить в константы, аминь. Хотя когда я смотрю на исходники всяких open source фреймворков, я вижу что хардкод там сплошь и рядом, но это делают они, а это делаю я, и я так не делаю.
Эпилог
Вот собственно и всё. API, на Бесплатном тарифе, обрабатывает запросы в последнюю очередь, поэтому иногда таймаута в 10 секунд не достаточно. В остальном работает стабильно. Я проверял.
Но наша цель конечно была не в этом, в результате выполнения задания, мы научились работать с JSON Web API и научились мутить фронтэнд с выводом информации в форму и передачей пользовательского ввода для обработки в серверный скрипт.
Ура :)
PostScriptum
Перед написанием статьи я чуть плотнее погуглил на тему «php json api мой склад» и нашёл массу реализаций, но не для JSON, а для XML. Из десятка найденных, парочка похожа на реально рабочие, но это не актуально потому что XML API грозятся отключить с 31 марта 2017.
А может всё будет так же как с «JSON API доступен для подписчиков на всех тарифах, кроме Бесплатного».
Конструктивная критика и ссылки на best practics ПРИВЕТСТВУЮТСЯ! мне не стыдно быть колхозником, но не хорошо бы если лучше?
Ссылки
Комментарии (45)
MetaDone
23.01.2017 08:41самое первое — https://getcomposer.org/download/
после — смотрите что такое psr-2 и psr-4
после — https://github.com/guzzle/guzzle
слишком много замечаний выйдет, нет смысла перечислять всеMetaDone
23.01.2017 08:45и логин с паролем можно вынести в конфиг
SbWereWolf
23.01.2017 10:28логин и пароль в конфиге, конфиг называется moysklad_curl_details.php.
зачем мне psr-4 в проекте на две функции? накидано всё в файл moysklad_routine_library.php, зачем огород городить с psr-4?
С psr-2 есть расхождения?
https://github.com/guzzle/guzzle — посмотрим, спасибо.MetaDone
23.01.2017 10:59+2странный конфиг.
зачем мне psr-4 в проекте на две функции?
чтобы хоть как-то структурировать код. сейчас там лапша, для одноразового скрипта, который выполнили и забыли — пойдет, но статья говорит про самообучение, потому надо привыкать делать все по устоявшимся в сообществе правилам.SbWereWolf
23.01.2017 12:52-1хоть как то код структурирован функциями, можно конечно вообще всё сделать функциями, а потом придумать классы и вообще неймспейсы как минимум один для логики приложения, другой неймспейс для общения с API, но это не про тестовое задание, это уже про «самозабвенно вырисовывать завихрения и завитушки архитектурных изысков» — этого мне на основной работе хватает.
про psr1/2/3 как бы нету замечаний? как бы я обычно в рамках общепринятых стандартов «творю».MetaDone
23.01.2017 13:36про psr-2 — используйте http://cs.sensiolabs.org/
psr-3 это про логгирование, что-то у вас я там ничего такого не увидел
тут не завихрения, а просто будет не лапша и возможность отделить мух от котлет
это как раз «про тестовое задание» — потому что в таком виде мало кто посчитает что оно выполнено.SbWereWolf
23.01.2017 14:25да логирования нет, psr-1/psr-2 обеспечиваются phpStorm / Code / Reformat не так ли? или «cs.sensiolabs.org» способен на большее?
oxidmod
23.01.2017 12:31SbWereWolf
23.01.2017 12:45мне показалось что это XML вариант?
oxidmod
23.01.2017 13:34мне кажется, что сменить xml на json не столь проблемно. Но тут есть неплохой костяк для разработки нормального API-клиента на php
SbWereWolf
23.01.2017 14:27не стояло задачи разработать API, но если бы что то уже было кем то разработано, то конечно было бы глупо не использовать, а взять за основу xml вариант и переделать его в json… надо сначала в коде разобраться, потом брать, мне кажется по времени такой подход не укладывается в два вечера после рабочего дня.
oxidmod
23.01.2017 14:33может немного дольше, но гораздо качественней. Тем более что код там вполне вменяемый и не требует лсишком много усилий чтобы его понять.
michael_vostrikov
23.01.2017 15:56Статья с пометкой "tutorial" и с кучей всяких "я не знаю" и "я не разобрался"? Вы серьезно?
SbWereWolf
23.01.2017 16:17вы считаете пометка how_to подойдёт больше?
michael_vostrikov
23.01.2017 16:45Я считаю, что статьи такого уровня вообще не стоит писать. Тем более в качестве руководства для других.
SbWereWolf
23.01.2017 17:42я бы подобные статьи с удовольствием читал если бы их кто то их писал, особенно я был бы рад им в поисковой выдаче Google, это лучше чем изобретать очередной велосипед, но мало кто такие элементарные вещи разжёвывает, потому что всем и так всё ясно.
michael_vostrikov
24.01.2017 10:09Вот как раз поэтому такие статьи и не нужны. Вот другой найдет эту статью, подумает "Это же Хабр, тут наверно правильные и качественные решения", накопирует ваш код и получит проблемы, технические или организационные в виде отказа в приеме на работу. Если уж писать, то писать надо правильный код с понятными объяснениями, а не "я не знаю, я не разобрался". Тогда это будет хороший обучающий материал.
Это не велосипед, а тестовое задание. Документация на API есть, учебников по PHP хватает. Получается, вы хотели бы не думать головой при выполнении тестового задания, а просто скопипастить готовое решение? Может цель задания и была в том, чтобы проверить, как вы сами организуете код для работы с таким API.
SbWereWolf
24.01.2017 10:34в работе не надо разрабатывать то что кто то уже разработал, не надо тратить время на то что бы разобраться с чужим кодом, если этот код работает без нареканий, надо тратить время на решение задач а не на проверку «инструментов», если что то работает, то надо этим пользоваться, а не разбирать по винтикам, что бы удостовериться что все винтики живые, ни один не разболтался и резьба ни где не сорвана. Разработка это умение комбинировать инженерные решения, а не умение потратить рабочее время на изобретение велосипеда.
michael_vostrikov
24.01.2017 11:12Ну вот у вас и была задача — сделать клиента для API, к которому готовых клиентов нет. Как вы будете на работе делать взаимодействие с API, которое в соседнем отделе разрабатывается? Попросите их специально для вашего проекта клиента написать? А вдруг вы сами в этом отделе будете работать, как вы клиента напишете?
SbWereWolf
25.01.2017 00:13ну вот я её и решил не тратя время на изучение тех вещей которые уже работают, в этом задании у меня был затык с фронт-эндом, плюс PHP за каким то чёртом заменяло пробелы на нижнее подчёркивание, вот это был ад, аж пришлось запариться на input:radio.
michael_vostrikov
25.01.2017 06:36Я говорил не об этом, а о качестве кода и обучающего материала, которые появились в процессе ее решения.
ilyaplot
23.01.2017 16:24Я совершенно не разделяю ваши методы разработки. Вот из-за таких, как вы, приходится обрабатывать невалидный YML через регулярки, а потом парсить.
схалявить и зафигачить JSON без json_encode, тупо вклеить
Мы же на хабре, тут такого не должно быть.SbWereWolf
23.01.2017 17:39схалявить и зафигачить JSON без json_encode, тупо вклеить
если следовать принципу Keep It Simple Stupid — тупо текст из документации к API это проще чем генерация этого текста.
Хотя генерация конечно методологически надёжней, но документация API после публикации не поменяется.
принцип You ain't gonna need it говорит о выполнении необходимого минимума действий для достижения цели. Если модификация не предполагается, то «статичный» текст полностью удовлетворяет «условиям задачи»
ellrion
23.01.2017 18:36Код очень стилистически соответствует самому изложению. Эдакое размашистое разгильдяйство. Исключительно по моим ощущениям конечно.
SbWereWolf
23.01.2017 18:45главное код тестами покрыть :) а когда нет тестов, то живые пользователи тоже сгодятся, но в этом случае важно успеть накатить бэкап раньше чем пользователю прочухают в чём дело: )
babylon
23.01.2017 21:57Развлекательно-отвлекательная статья. То, что автор хотя бы думает в направлении JSON API уже замечательно. Раз есть комментарии, то автор замечен по любому. На хабре много самоучек и самоделок. Это не плохо и не хорошо. Это состояние индустрии. Заглянул давеча на сайт, посвященный известной игровой платформе. Но люди почему то неутомимо и упорно пишут свой самокат без педалей. Видимо всё таки зависит от возраста и от опыта. Я еще раз вспомнил о Jquery. Это ширпотреб в хорошем смысле, чего пока нельзя сказать о JSON API и Angular. Я — за ширпотреб. Можно с тестами можно без них. Тесты не спасают от неправильной парадигмы мышления и выбора средств.
SuperDoker
Смешивать HTML и PHP не очень хорошая идея, советую разобраться в вопросе и сделать то же самое только правильно
SbWereWolf
можно пример правильной «ручной» реализации? без использования фреймворка?
ilyaplot
function render($template, $data)
{
extract($data);
ob_start();
require $template;
return ob_get_clean();
}
Шаблон пишется на чистом PHP.
SbWereWolf
спасибо.
SbWereWolf
мои статьи по SQL набирали 6к/9к/5к просмотров, у этой статьи всего 1.7к, не очень то люди и заметили, а кто заметил, тот влепил минус — как это принято ни кто не смотрел исходники выложенные на гите, и приняли код приведённый в статье за чистую монету, отсюда эти… не обоснованные обвинения в «лапше», в проекте вообще 4 файла:
на мой вкус мухи отдельно, котлеты отдельно.
но у людей голова ломается от отсутствия классов, от отсутствия автозагрузчика, видимо когда то им за это хорошенько досталось и теперь они комплексуют, хорошо хоть не обвинили в отказе от фремворка.
Единственный человек по делу написал это ilyaplot — ещё раз спасибо.
Очень жаль что ни кто не прокомментировал проблему с пустым POSTом но «полным» — php://input
такое резюме по отклику на статью.
ЗЫ
нет что бы поржать вместе со мной, зачем то обосрали, видимо потребность в «срать» имеет приоритет над «ржать» :)
michael_vostrikov
Судя по рейтингу и комментам, ваши статьи по SQL тоже мало чего полезного содержат. Раз у этой статьи минусов больше, то может и полезного в ней меньше?
Код в статье ничем не отличается от исходников на github. И код этот не очень хороший, и по стилю, и по архитектуре, и по логике работы.
Воот. А потом в рабочем проекте добавятся еще 100500 глобальных функций, и окажется, что неплохо было бы их еще разделить — работа с curl, с массивами, с валютой и т.д. Так может сразу использовать классы и пространства имен?
Оно очень даже тесное, так как у вас нет HTML-разметки, она выводится в строках PHP-кодом, с соответствующими слэшами возле кавычек.
Почему вы так уверены, что люди не писали без классов и автозагрузчика? Как раз писали, и знают, к чему это может привести.
Извините, вам здесь разве обязаны помогать? Ваш код, сами разбирайтесь, вы же программист. Тем более что это ошибка из-за вашего незнания основ. Об этом ниже.
Ни в вашей статье, ни в ваших комментах с негативным отношением к окружающим нет ничего смешного.
michael_vostrikov
По коду. И нет, отмазка "это же тестовое задание, тут надо просто чтоб работало" не прокатит. Его дают не за этим, а как раз наоборот — чтобы проверить, как вы пишете код.
— Неправильно расставлены скобки, пробелы и отступы. PHPStorm, о котором вы говорили ниже, вам видимо не очень помогает. Имена переменных и фукнций не соответствуют их цели — например, $сounterparty это массив, а название в единственном числе.
—
echo 'Доступные контрагенты:<br />';
Зачем вы пишете разметку в строках? Вам лень тег php закрыть?)
—
'<label for="' . $personId . '">' . $person['name'] . '</label>'
Потенциальная XSS-уязвимость. Что если кто-нибудь заведет контрагента с названием "
<script>[jscode]</script>
"?—
$counterpartyId = $counterpartyIdCollection[FIRST_INDEX];
Зачем вы отправляете массив, если вам нужен только первый элемент? Также зачем-то проверяются data-organization-type и data-counterparty-type, хотя они везде установлены в "1".
—
contentType: 'application/json; charset=utf-8'
Вот и ваша проблема с POST. PHP не принимает данные в формате JSON (документация). Надо убрать contentType, jQuery сам подставит правильный, данные отправлять через
$('#orderForm').serialize()
, и поменять обработку на сервере, потому что данные сериализуются немного в другом формате.— Работа функции getNomenclature() не соответствует названию. Ее результат полностью зависит от того, что мы вызывали до нее, так что может вернуть и nomenclature и order и все что угодно. Надо перенести установку URL в нее, и в остальных функциях аналогично. А можно и в класс объединить, тогда снаружи вместо этого
будет вот так
Все просто и понятно.
От нечего делать сделал небольшой рефакторинг этого задания (хотя там много чего еще можно поменять), не столько для вас, сколько для тех, кто потом найдет в поиске эту статью:
коммиты, разметка, отправка формы.
SbWereWolf
видимо
контрагентов заводит владелец аккаунта, хочет себе в ногу выстрелить — сколько угодно, хотя я думаю разработчики API такую возможность уже исключили
если всё можно обработать по одной технологии то зачем придумывать две? я ни когда не ставлю перед собой цели оптимизации с выгадыванием миллисекунд
спасибо огромное, за это вам можно все наезды простить :)
соответствует, предполагалось, что вернётся чёрти что и это надо будет как то парсить, но во всех трёх случаях и с организацией и с контрагентами и с номенклатурой всё оказалось намного проще, можно было конечно из API_шного ответа выдирать только айдишник и имя, пихать их в массив и его возвращать, но для меня этот проект был почеркушкой а не фундаментом для дальнейшей разработки и ваш серьёзный подход это просто последующие итерации которые происходят, тогда когда они нужны, в данном случаи они были не нужны.
спасибо, ваш разбор полётов был одной из целей этой публикации
michael_vostrikov
Вам сложно будет пройти тестовое задание с таким подходом. Никому неохота разбираться в особеностях названий чужого кода.
Разработчикам API такая уязвимость не грозит, они просто данные отправляют.
А оптимизация здесь ни при чем, это банально лишний код.
(полная версия)
Вы не поняли. getNomenclature() просто выполняет запрос с текущими настройками curl (и кстати ничем не отличается от getCounterparty(), у них абсолютно одинаковый код). Если я перед ее вызовом установлю другой URL, то вернутся другие данные, с номенклатурой не связанные.
SbWereWolf
отличается, в контрагентах из полученного массива сохраняются только 'rows', в номенклатурах такой обработки нет, она идёт снаружи метода.
мне бы ваши познания в jquery :)
у этого тестового была цель выполнить в оговоренный срок, а не качественно написать, у меня отведённое время ушло на «борьбу» с PHP по обработке $_POST, от которой пришлось отказаться и на работу с jquery, на то что бы причесать код времени не осталось
задание принимал бизнес-аналитик / проджект менеджер который собирает команду фрилансеров, ему в срок важнее, качества кода он не оценит.
michael_vostrikov
И суть не в этом, а в том, что они не делают то, что отражено в названии. Если я просто вызову getCounterparty(), я не получу никакой сounterparty.
Для статьи код можно было причесать, при ее написании вас никто не торопил.
Качество оценит команда фрилансеров, которые будут с вами работать. И читатели статьи.
Тут достаточно уметь гуглить, «jquery select by name» и «jquery get id attribute». Хотя не вижу принципиальных отличий от ваших селекторов.
Если вы не знаете PHP и jQuery, зачем тогда писать обучающие статьи.
SbWereWolf
мои обстоятельства меня торопили, на статью ушло около 4-х часов, это пол воскресения, а ещё надо был она работе выправить ножки на материнке, в подъезде организовать установку замка на чёрный ход в подъезд и вообще могу я в воскресение отдаться безделью?
принципиальное отличие межу мои и вашим вариантами только в опыте автора решения в использовании jquery
«на пять не знает даже Бог», если что. Код решает задачу, и я в статье объясняю технологию решения, чем не обучение, учу плохому? вы внесли свою лепту что бы статья стала учить хорошему :)
michael_vostrikov
Можно было вообще статью не писать, было бы больше свободного времени) И вы думаете, у других авторов нет своих дел? Тем не менее, они уделяют внимание коду, который будут читать другие.
jQuery и опыт тут ни при чем. Если бы вы хотели получить одно значение, а не массив, вы бы поискали информацию и нашли решение. Тем более что это первые ссылки в гугле.
Вот как раз не объясняете, а говорите "я не знаю" и "я не разобрался".
То, что кто-то решил исправить ошибки, не означает, что надо писать плохие статьи. Особенно они вредны для новичков, которые не всегда могут разобраться, что там правильно, а что нет.
SbWereWolf
у Гугла поисковая выдача заточена под пользователя, у когото это первая десятка записей, у кого то не первая, я по jQuery раз в пол года ищу, у меня всякая фигня в первой десятке.
статья про работу с API, как работать с API я разобрался.
Пишите хорошие, а то приходиться писать плохие — свято место пусто не бывает :))
oxidmod
скрин первой страницы выдачи?)
jquery select by name
jquery get id attribute
я вообще очень едко гуглю чтото по jquery и 1-2 линка на https://api.jquery.com
michael_vostrikov
Ваши отмазки выглядят глупо. Думаю, программист смог бы отличить фигню от нефигни, да и вообще найти ответ на такой простой вопрос. Значит вы и не пытались искать. И да, проверил через VPN и анонимайзер в приватном окне браузера и в Internet Explorer, которым не пользуюсь. И в google и в yandex и даже в bing есть нужные ссылки.
Про работу с API написано в документации к API. Зачем делать статью с точно такой же информацией? Может ценность как раз и должна быть в коде для работы с ним?
Отсутствие хороших статей по API Мой Склад не означает, что надо обязательно писать плохие. А если решили написать, то тогда не возмущайтесь на минусы.
SbWereWolf
:))