JQuery Promise. Как заставить промисы работать на себя

Одна из самых важных и популярных задач в PHP - передача данных. Вы, наверное, не раз сталкивались с url вроде site.ru/page.php?id=114841 или формой с полями ввода текста (регистрация, новый комментарий и тд). В этом и следующем уроках поговорим об передаче данных в PHP .

Метод POST в PHP Метод состоит из двух частей: HTML c формой и полями и файла-обработчика на PHP.

Внимание! Для работы нам достаточно создать соответственно всего два файла .

Демонстрация Скачать исходники
Рассмотрим пример с комментариями - реализация авторизации пользователя на сайте:

Код HTML (файл post.html)



Форма





Ваш логин:

Ваш пароль:





Теперь подробнее о методах передачи данных. Существует два вида GET (через url, открытый - можно изменить url вручную) и POST (через форму, закрытый). Отличие будет заключаться в содержании адресной строки, то есть url.

При получении данных переданных одним из этих способов в массив соответствующего методу типа ($_GET или $_POST) собираются данные. Также существует массив $_REQUEST, который может содержать и $_GET, и $_POST одновременно. Но это для других примеров. Рекомендую большинство данных передавать методом POST.

Теперь рассмотрим код обработчика.

Код PHP (в файле test_reg.php)


Вы можете видеть как в отдельные переменные мы записываем значение полей с соответствующими name в массиве $_POST, хотя также можно было собрать и в $_REQUEST.

В обработчике, пожалуй, проработаем условие авторизации - совпадают ли логин и пароль в форме с нашим выдуманным (соответственно Admin и Pass). И либо поздороваемся с входящим (Здравствуйте, Admin! Сегодня кофе или чай?), либо нет (Вы ввели неверную связку логин-пароль. Попробуйте ещё). Однако для создания полноценной авторизации Вам ещё надо ознакомиться с cookie, сессиями и . Но об этом позже.

Непосредственно в скрипте можно как угодно обрабатывать данные: дописывать, стирать, шифровать и так далее. Главное - это знать имя переменной (задаётся в HTML-форме) и дальше собирать их с помощью массивов $_POST, $_GET и $_REQUEST.

Спасибо за внимание!

январь 26 , 2017

Промисы, обещания, ад из колбэков, объект Deferred, then-ы, when-ы и резолвы...
Эти слова доносятся из каждого телеграфного столба. Есть ощущение, что я последний оболтус на этой планете, который не пользуется промисами. Погрустив на сей счет, я затеял разобраться с этой темой. Но как и в случае с git rebase выяснилось, что в интернетах много информации о промисах, но мало где есть объяснение на пальцах. А раз в интернетах чего-то нет, надо создать это самому.

Меня мало интересует общая теория и тонкости этой замечательной штуки. Любую незнакомую вещь я воспринимаю с точки зрения возможной пользы от нее. Поэтому подробные объяснения механизма работы промисов лучше поискать в других блогах. Мы же с вами рассмотрим пример практической работы с jquery promise. И в итоге выясним, стоит ли разбираться с этим подробнее или же можно дальше законно писать тонны кода в $.ajax.success.

Когда нам вообще могут понадобиться промисы?

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

В нашем воображаемом проекте есть сущность Пользователь, по-программистски user, и Заказ - order. И есть несколько get и post-запросов, выполняющих такие операции:

  • 1. GET php/user/info.php - получение информации о пользователе
  • 2. GET php/user/orders.php - получение заказов пользователя
  • 3. POST php/user/new.php - добавление нового пользователя
  • 4. POST php/order/new.php - добавление заказа
  • 5. POST php/order/applyBonus.php - применение бонуса к заказу (списание некой суммы с определенного заказа)

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

Даже если нам нужно отправить один запрос и в success выполнить некий код - уже выглядит не очень хорошо. А если взять результат первого запроса и отправить его во второй? А если нужно последовательно выполнить 2 запроса, дождаться получения данных, эти данные использовать в третьем, и только после этого выполнить какие-то действия? Ужас полный.

Вы спросите: что же это за дикие требования, где нужно последовательно выполнить 3 ajax-запроса и только потом совершить какое-то полезное действие? А сейчас мы набросаем десяток задач, для решения которых задействуем промисы. Задачи будут усложняться и последней выйдет эта самая тема из трех последовательных запросов. И самое главное, мы напишем код, который позволит вменяемо управлять любыми комбинациями ajax-запросов. Приступим.

Какие задачи мы будем решать?

Оглашаю весь список:

  • 1. Простое получение информации о пользователе - 1 get-запрос
  • 2. Получение списка заказов пользователя - 1 get-запрос
  • 3. Получение последнего заказа - 1 get-запрос + обработка данных на клиенте
  • 4. Общая сумма всех заказов пользователя - 1 get-запрос + обработка данных на клиенте
  • 5. Добавление пользователя - 1 post-запрос
  • 6. Добавление заказа - 1 post-запрос
  • 7. Добавление заказа с регистрацией пользователя - 2 post-запроса. Второй запрос использует результаты первого.
  • 8. Применение бонуса к заказу - 1 post-запрос
  • 9. Применение бонуса к последнему заказу пользователя - 2 get и 1 post-запрос

Как видим, большинство задач великой сложности не представляют. Их вполне можно решить и обычными $.ajax.success колбэками. Но во-первых, Ваш код будет быстро захламлен, во-вторых, его сложно будет переиспользовать, а в-третьих, только представьте, какую портянку придется написать для решения задачи номер 9 с тремя последовательными запросами.

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

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

Пишем серверный код. Конечно же, PHP

Набросаем на скорую руку 5 php-скриптов. А заодно разберем, какие параметры они будут принимать и что возвращать. В корне проекта создадим папку php, а в ней еще 2 - user и order. И по порядку рассмотрим все файлы-запросы.

PHP. Получение информации о пользователе

GET /php/user/info.php

If ($_SERVER["REQUEST_METHOD"] === "GET") { // Принимаем данные из массива $_GET $userId = (int)$_GET["userId"]; // Вытаскиваем информацию о пользователе из базы... sleep(1); // Возвращаем email и бонус пользователя echo json_encode(array("email" => "[email protected]", "bonus" => rand(500, 1000))); } else { header("HTTP/1.0 405 Method Not Allowed"); echo json_encode(array("error" =>

Принимаем на вход параметр userId, затем ждем 1 секунду (имитируем какую-то полезную работу) и возвращаем email и бонус пользователя в json-объекте. Сгенерируем бонус рандомом, чтобы было не так грустно следить за одними и теми же числами.

Есть один момент: если попытаемся обратиться к скрипту не GET-запросом, то вернем http-ошибку 405 с соответствующим текстом в поле error. Пока не обращайте внимания, вспомним об этом в конце статьи.

Конечно, в реале у Вас будет написан код, лезущий в базу, но этот пост не о том. Сейчас нам достаточно лишь сымитировать запросы, уделив немного внимания входным параметрам и формату ответов от сервера. Все это пригодится при написании js-кода с промисами. Оставшиеся 4 апи-запроса мы напишем по аналогии.

PHP. Список заказов пользователя

GET /php/user/orders.php

If ($_SERVER["REQUEST_METHOD"] === "GET") { // Принимаем данные из массива $_GET $userId = (int)$_GET["userId"]; // Вытаскиваем информацию о пользователе из базы... sleep(1); // Возвращаем заказы пользователя echo json_encode(array("orders" => array(array("orderId" => rand(1, 5), "summa" => rand(1000, 1500)), array("orderId" => rand(10, 20), "summa" => rand(2000, 5000)), array("orderId" => rand(30, 50), "summa" => rand(10000, 20000))))); } else { header("HTTP/1.0 405 Method Not Allowed"); echo json_encode(array("error" => "Метод не поддерживается")); }

Принимаем userId, отдаем массив из объектов с двумя полями: id и сумма заказа.

PHP. Создание пользователя

POST /php/user/new.php

If ($_SERVER["REQUEST_METHOD"] === "POST") { // Принимаем данные из массива $_POST $email = $_POST["email"]; $name = $_POST["name"]; // Типа добавляем пользователя в базу... sleep(1); // Возвращаем id созданного пользователя, для примера - random echo json_encode(array("userId" => rand(1, 100))); } else { header("HTTP/1.0 405 Method Not Allowed"); echo json_encode(array("error" => "Метод не поддерживается")); }

На входе из массива $_POST берем email и имя, возвращаем id созданного пользователя.

PHP. Добавление заказа

POST /php/order/new.php

If ($_SERVER["REQUEST_METHOD"] === "POST") { // Принимаем данные из массива $_POST $userId = (int)$_POST["userId"]; $summa = (int)$_POST["summa"]; // Добавляем заказ в базу... sleep(1); // Возвращаем id созданного заказа, для примера - random echo json_encode(array("orderId" => rand(1, 1000))); } else { header("HTTP/1.0 405 Method Not Allowed"); echo json_encode(array("error" => "Метод не поддерживается")); }

Принимаем id пользователя и сумму заказа, возвращаем id созданного заказа.

PHP. Применение бонуса к заказу

POST /php/order/applyBonus.php

If ($_SERVER["REQUEST_METHOD"] === "POST") { // Принимаем данные из массива $_POST $orderId = (int)$_POST["orderId"]; $bonus = (int)$_POST["bonus"]; // Добавляем заказ в базу... sleep(1); // Возвращаем код успеха echo json_encode(array("code" => "success")); } else { header("HTTP/1.0 405 Method Not Allowed"); echo json_encode(array("error" => "Метод не поддерживается")); }

На вход - id заказа и сумма бонуса, на выходе просто код успеха, мол, все хорошо прошло.

С подготовкой закончили, переходим к клиентской части и js-коду.

Каркас проекта и html-заготовка

Создадим файл index.html в корне проекта. В секции head напишем такое

Promise jQuery

Немного стилей прямо в секции head - пусть будет так. В body поместим следующее.

Promise jQuery

1. Общая информация о пользователе 2. Список заказов 3. Последний заказ 4. Общая сумма всех заказов 5. Добавление пользователя

6. Добавление заказа 7. Добавление заказа с созданием пользователя 8. Применение бонуса к заказу 9. Применение бонуса к последнему заказу пользователя

Здесь видим 9 кнопок, по клику на которые запускаются вышеозначенные операции. С каждым разом задачи будут усложняться. Реализовывать функционал мы будем последовательно. А в конце с удивлением заметим, что написали функцию, которая дождалась выполнения двух ajax-запросов и использовала их результаты в третьем, а после завершения третьего сделала что-то еще... Звучит тяжко, но это окажется не сложнее, чем отправить один-единственный get-запрос за данными. Объем кода для всех 9-ти задач будет примерно одинаковый - по десятку простых строк.

В конце index.html подключены 4 js-файла. jquery закиньте в папку js сразу, файлы user.js и order.js пока оставьте пустыми. А с main.js мы немного поработаем и напишем базовый код инициализации нашего приложения.

Заготовка main.js

Общая структура файла такова

// Главный модуль приложения "use strict"; var app = (function() { var userId = Math.round(Math.random() * 100); // Получаем информацию о пользователе function _userInfo() { // ... } // Получаем список заказов пользователя function _userOrders() { // ... } // Последний заказ пользователя function _userLastOrder() { // ... } // Общая сумма всех заказов function _userTotalSummaOrders() { // ... } // Добавление пользователя function _userNew() { // ... } // Добавление заказа function _orderNew() { // ... } // Добавление заказа с созданием пользователя function _orderNewWithUser() { // ... } // Применение бонуса к заказу function _orderApplyBonus() { // ... } // Применение бонуса к последнему заказу пользователя function _userApplyBonusToLastOrder() { // ... } // Ловим ошибки в промисах function onCatchError(response) { // ... } // Навешиваем события на кнопки function _bindHandlers() { $("#user-info-btn").on("click", _userInfo); $("#user-orders-btn").on("click", _userOrders); $("#user-last-order-btn").on("click", _userLastOrder); $("#user-total-summa-orders-btn").on("click", _userTotalSummaOrders); $("#user-new-btn").on("click", _userNew); $("#order-new-btn").on("click", _orderNew); $("#order-new-with-user-btn").on("click", _orderNewWithUser); $("#order-apply-bonus-btn").on("click", _orderApplyBonus); $("#user-apply-bonus-to-last-order-btn").on("click", _userApplyBonusToLastOrder); } // Инициализация приложения function init() { _bindHandlers(); } // Возвращаем наружу return { onCatchError: onCatchError, init: init } })(); // Запускаем приложение $(document).ready(app.init);

Здесь мы подготовили модуль app - главный модуль приложения. Переменная userId, глобальная для всего модуля, для примера берется рандомно. Дальше перечислены 9 функций, которые запускаются по нажатию на соответствующие кнопки. По комментариям Вы их легко отследите.

Функция onCatchError срабатывает, когда что-то в ajax-запросах пойдет не так. _bindHandlers навешивает все 9 событий кликов, init - базовая функция инициализации модуля. Блок return возвращает фасад модуля, то есть методы, доступные извне. onCatchError возвращается наружу, потому что будет использоваться в модулях user и order. Дальше идет $(document).ready(app.init) - это запускает приложение.

Схема такая: непосредственно код, получающий данные и отправляющий их на сервер, будет располагаться в модулях user.js и order.js. То есть там основная реализация, ядро приложения. А в main.js в соответствующих местах, где сейчас стоит //... будет написано по несколько строк кода, использующего возможности модулей user и order.

Не волнуйтесь, если не до конца вкурили, сейчас все станет ясно.

Получаем информацию о пользователе

Первая задача - отправить ajax-запрос на сервер, получить инфу и что-то с ней сделать. Казалось бы чего здесь умничать? Мы тыщу раз так писали

$.ajax({ url: "php/user/info.php", data: { userId: userId }, method: "get", dataType: "json", success: function(response) { // Что-то сделать с response }, error: function(response) { // Что-то сделать при ошибке }, });

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

Файл /js/user.js

// Модуль Пользователь "use strict"; var user = (function() { // Общая информация о пользователе // Возвращаем объект - email и бонус пользователя function getInfo(params) { return $.ajax({ url: "php/user/info.php", data: { userId: params.userId }, method: "get", dataType: "json" }).then(function(data) { return { email: data.email, bonus: data.bonus }; }).fail(app.onCatchError); } // Возвращаем наружу return { info: getInfo } })();

Это каркас модуля user и первая функция, использующая промисы. Выглядит очень знакомо - тот же самый $.ajax, но для начала нет функции-колбэка. Вместо этого указываем, какие данные возвратить по результатам запроса. В блоке then ... return мы говорим, что ждем от промиса объект из двух полей: email и bonus. Как помним, именно они возвращаются с бекенда по запросу php/user/info.php.

getInfo(params) - в функцию передаем объект с параметрами. В нашем случае он один-единственный, userId, но Вы понимаете, что объект удобен тем, что в нем можно передать сколько угодно данных. Поэтому и в дальнейшем мы будем использовать объекты в качестве параметров. Впрочем, и в качестве ответа от сервера тоже.

Fail(app.onCatchError) говорит о том, что в случае каких-то неполадок с запросом нужно вызвать метод onCatchError модуля app. Неполадки могут быть разные: ошибка на стороне сервера, неправильный метод запроса или неверный url. Что делает этот метод onCatchError, пока не обращайте внимания, разберемся в конце статьи.

Еще заметьте, что метод user.info ничего не знает о том, как будут использоваться данные. Его задача - данные вернуть, а что с ними делать, пусть разбирается код, вызвавший user.info. И на мой взгляд, это круто, потому как видно четкое разделение логики. Модуль user знает, как взаимодействовать с сервером, но не знает, что хочет от него браузер. Этот модуль можно представить как прослойку между javascript-манипуляциями с dom-браузера и нашим бекендом. Точно так же, как и php-код является такой же прослойкой между браузером-клиентом и базой данных.

Но мы увлеклись созерцанием модуля и getInfo. Давайте взглянем, как использовать его на практике - это в функции _userInfo модуля app. Мы заботливо написали заглушку для нее, давайте же заполним кодом

// Получаем информацию о пользователе function _userInfo() { user.info({userId: userId}).done(function(userInfo) { console.log("userInfo: ", userInfo); }); }

А это все, что вызывается при клике на кнопку! Мы дергаем метод info модуля user. Не getInfo - это внутренняя функция. Именно info, так как мы ее указали в user в блоке return {...}

user.info({userId: userId}) возвращает нам промис. Дальше по цепочке вызываем done с функцией-колбэком, в которую передаем результат промиса. Напомню, это объект вида {email: "[email protected]", bonus: 12345}. Ничего особенного делать с этим результатом не будем, просто выведем в консоли. Но никто не мешаем Вам выполнить с этими данными какие-то манипуляции. Возможно даже выделить для этого отдельную функцию, в которую передадите объект userInfo.

Давайте посмотрим еще раз на получившийся код и сравним его с привычным $.ajax запросом и выполнением всех нужных манипуляций в success-e. Вроде бы код тот же самый, разве что его стало немного больше. Но что-то все же изменилось. Код стал чище. Он разделился на 2 логические части: получение данных и их обработка. Меньше вложенности. Функции короче.

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

Получаем список заказов пользователя

Добавим в модуль user такой код

// Список заказов пользователя // Возвращаем массив заказов формата {id заказа, сумма заказа} function getOrders(params) { return $.ajax({ url: "php/user/orders.php", data: { userId: params.userId }, method: "get", dataType: "json" }).then(function(data) { return data.orders; }).fail(app.onCatchError); } return { info: getInfo, orders: getOrders, }

А в main.js заполним кодом еще одну функцию

// Получаем список заказов пользователя function _userOrders() { user.orders({userId: userId}).done(function(userOrders) { console.log("userOrders: ", userOrders); }); }

Здесь ничего нового, отправили запрос, получили список заказов, вывели в консоль. Но вот уже в следующем абзаце будет намного интереснее.

Получаем последний заказ пользователя

Допустим, нам нужна информация не о всех заказах, а об одном, последнем. Бэкендское апи позволяет нам получить только все заказы целиком, в массиве. Проблем нет, мы легко вытащим отдельный объект заказа на клиенте, но как это организовать?

Конечно, нам помог бы тот же $.ajax, но сколько бы кода пришлось нам накопипастить при этом? Мы же не знаем, как часто придется вытаскивать последний заказ и в какие функции отправлять полученные данные. Конечно, можно упорядочить любой код, написать функции-обертки для ajax-запросов, и пробрасывать в $.ajax.success нужные функции-обработчики результатов... Но согласитесь, это не самый удобный способ, хотя и привычный

Поэтому мы в свете осваивания промисов сделаем такой финт ушами и напишем еще одну функцию в модуле user

// Последний заказ пользователя // Возвращаем последний заказ (объект) function getLastOrder(params) { return getOrders(params).then(function(orders) { return orders.slice(-1); }); } return { info: getInfo, orders: getOrders, lastOrder: getLastOrder, }

Посмотрите, что мы сделали. Мы взяли уже готовый метод, возвращающий другой промис, получащий список заказов. В orders в.then(function(orders)) попадает именно массив заказов, полученный с бекенда. Дальше лишь осталось вернуть последний элемент массива через return orders.slice(-1);

Преимущество такой цепочки видно уже здесь. Функция getLastOrder даже не знает, каким образом вытаскиваются все заказы. Здесь не видно ajax-запроса, этим занимается другой метод. Мы только пользуемся готовым результатом.

А использование этого в main.js при клике на кнопку такое же простое, как и в предыдущих примерах

// Последний заказ пользователя function _userLastOrder() { user.lastOrder({userId: userId}).done(function(lastOrder) { console.log("userLastOrder: ", lastOrder); }); }

Давайте напишем еще одну функцию для закрепления.

Получение суммы всех заказов пользователя

// Общая сумма заказов function getTotalSummaOrders(params) { return getOrders(params).then(function(orders) { return orders.reduce(function(total, currOrder) { return total + currOrder.summa; }, 0); }); } return { info: getInfo, orders: getOrders, lastOrder: getLastOrder, totalSummaOrders: getTotalSummaOrders }

// Общая сумма всех заказов function _userTotalSummaOrders() { user.totalSummaOrders({userId: userId}).done(function(totalSumma) { console.log("userTotalSummaOrders: ", totalSumma); }); }

Мы использовали тот же приемчик. Взяли готовый метод getOrders(params) и высчитали сумму заказов через reduce. И опять мы видим вместо десятка строк кода с колбеками 2 коротких и внятных функции. К тому же user.totalSummaOrders() можно и дальше использовать где угодно, не заботясь о том, как будут использоваться возвращаемые ей данные.

Добавление нового пользователя

// Добавление пользователя // Возвращаем id созданного пользователя function newUser(params) { return $.ajax({ url: "php/user/new.php", data: { // какие-то данные о новом пользователе email: params.email, name: params.name }, method: "post", dataType: "json" }).then(function(data) { return data.userId; }).fail(app.onCatchError); } return { info: getInfo, orders: getOrders, lastOrder: getLastOrder, totalSummaOrders: getTotalSummaOrders, newUser: newUser }

// Добавление пользователя function _userNew() { var data = { email: "[email protected]", name: "Webdevkin" }; user.newUser(data).done(function(userId) { console.log("userNew: ", userId); }); }

Выглядит чуть длиннее, но только из-за того, что мы передаем на сервер объект из двух полей. В остальном код такой же простой, как и при получении информации. Не забываем, что здесь уже используется method: "post"

А теперь еще интереснее, начнем комбинировать ajax-запросы. Создадим новый модуль order.js.

Добавление заказа

// Модуль Заказ "use strict"; var order = (function() { // Добавление заказа // Возвращаем id созданного заказа function _createOrder(params) { return $.ajax({ url: "php/order/new.php", data: { // какие-то данные о новом заказе userId: params.userId, summa: params.summa }, method: "post", dataType: "json" }).then(function(data) { return data.orderId; }).fail(app.onCatchError); } // Добавление заказа // Возвращаем id созданного заказа function newOrder(params) { if (params.userId) { // userId известен, сразу добавляем заказ return _createOrder(params); } else { // Нужно создать сначала пользователя return user.newUser(params).then(function(userId) { return _createOrder({userId: userId, summa: params.summa}); }); } } // Возвращаем наружу return { newOrder: newOrder } })();

Здесь сразу 2 функции. _createOrder - это непосредственно добавление заказа. Работает аналогично с добавлением пользователя. Напрямую извне вызывать этот метод мы не будем - вся работа пойдет через newOrder.

Чем же таким особенным она занимается? Здесь нужно небольшое отступление...

Как работает оформление заказа в интернет-магазинах?
Часто оформление заказа проходит по двум схемам: для зарегистрированных пользователей и гостей. Для зарегистрированных все понятно: берем userId (как правило, он хранится на бекенде в сессии), передаем с клиента данные о заказе, добавляем заказ в базу. Лепота.
Но если на сайт зашел "гость", то ему мы тоже должны обеспечить оформление заказа. Но завести на него пользователя все-таки нужно, заказ нужно тупо к кому-то привязывать. Вы можете заставить человека отдельно зарегистрироваться на сайте, вернуться к оформлению, и уже как полноправному пользователю отправить-таки данные о заказе на сервер.
А можно поступить гуманно: собрать минимум данных о пользователе (имя + email), данные о заказе и одним махом и зарегистрировать пользователя, и добавить ему заказ. Нет лишних телодвижений - больше радости покупателю.

Короче, получается, что функция newOrder умеет выполнять не только добавление заказа, но и предварительно добавить пользователя. Эта необходимость проверяется наличием параметра userId в params. Если его нет, то мы сначала добавляем пользователя, получаем его userId и уже с ним запускаем добавление заказа _createOrder

Если же params.userId известен, то _createOrder запускается напрямую. Достаточно сложные манипуляции реализуются теми же 5-6ю строками кода. С каждым разом мы берем на себя все более сложные задачи, но не наблюдаем существенного усложнения кода. Набор простейших методов, возвращающих промисы, позволяет нам как угодно комбинировать запросы и собирать сложный функционал как конструктор Лего.

Проверим, как работает добавление заказа в app.js - напишем обработчики еще для двух кнопок в main.js.

// Добавление заказа function _orderNew() { var data = { userId: userId, summa: 7000 }; order.newOrder(data).done(function(orderId) { console.log("orderCreate: ", orderId); }); } // Добавление заказа с созданием пользователя function _orderNewWithUser() { var data = { email: "[email protected]", name: "Webdevkin", summa: 10000 }; order.newOrder(data).then(function(orderId) { console.log("orderNewWithUser: ", orderId); }); }

Применяем бонус к заказу

// Применение бонуса к заказу // Возвращаем true в случае успеха function applyBonus(params) { return $.ajax({ url: "php/order/applyBonus.php", data: { orderId: params.orderId, bonus: params.bonus }, method: "post", dataType: "json" }).then(function() { return true; }).fail(app.onCatchError); } // Возвращаем наружу return { newOrder: newOrder, applyBonus: applyBonus }

// Применение бонуса к заказу function _orderApplyBonus() { order.applyBonus({orderId: 5, bonus: 200}).then(function(result) { console.log("orderApplyBonus: ", result); }); }

Здесь все знакомо. Функция нужна нам как промежуточная, чтобы продемонстрировать, как выполнить 3 ajax-запроса. Нужно лишь придумать случай надобности 3 запросов подряд.

Да легко! Пофантазируем. На день рождения фирмы руководство решило начислить бонус всем своим клиентам и списать эту сумму с последнего заказа пользователя. Как бонус начислился, не наше дело, нам нужно получить сумму бонуса и id последнего заказа. Это 2 get-запроса, которые можно выполнить параллельно. Дождавшись этих данных, мы отправим еще один (третий) запрос на сервер, уже post, который и применит указанный бонус к нужному заказу.

Пример, конечно, притянут за уши, но ради искусства сойдет. Пишем код.

Применение бонуса к последнему заказу пользователя

// Применение бонуса к последнему заказу // Возвращаем true в случае успеха function applyBonusToLastOrder(params) { var infoPromise = this.info(params), lastOrderPromise = this.lastOrder(params); return $.when(infoPromise, lastOrderPromise).then(function(userData, lastOrderInfo) { return order.applyBonus({ orderId: lastOrderInfo.orderId, bonus: userData.bonus }); }); } return { info: getInfo, orders: getOrders, lastOrder: getLastOrder, totalSummaOrders: getTotalSummaOrders, newUser: newUser, applyBonusToLastOrder: applyBonusToLastOrder }

Здесь мы использовали новую конструкцию $.when. Смысл в том, что мы передаем ей несколько промисов, ждем их результатов, а в функцию then передаем колбек - что хотим с этими результатами делать. В нашем случае мы из первого вытаскиваем бонус пользователя, а из второго - id последнего заказа. И отдаем эти данные в третий метод. И все.

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

Проверяем работу в main.js так

// Применение бонуса к последнему заказу пользователя function _userApplyBonusToLastOrder() { user.applyBonusToLastOrder({userId: userId}).then(function(result) { console.log("userApplyBonusToLastOrder: ", result); }); }

Обработка ошибок

Наш мир не идеален, и на любом этапе запрос может прерваться. Нам нужно как-то обработать такие случаи и вспомнить про метод app.onCatchError. Давайте посмотрим на его реализацию

// Ловим ошибки в промисах function onCatchError(response) { var json = response.responseJSON, message = (json && json.error) ? json.error: response.statusText; console.log("ERROR: ", message); }

Что здесь происходит? В параметре response мы найдем информацию о настигнувшей нас ошибке. Сначала мы вытаскиваем содержимое response в объект json. А дальше проверяем его на наличие непонятного поля error.

Если мы вернемся в раздел с php-кодом, то увидим, что ничего загадочного в этом поле нет. Мы сами отдаем его с бекенда в случае несоответствия метода (http code 405). Когда, например, мы пытаемся создать пользователя методом GET. Я написал этот php-шный код исключительно чтобы показать, что мы сами можем формировать адекватные причины ошибок и сообщать о них пользователям. Точно так же можно выбрасывать такой ответ, если возникнет ошибка при валидации входных параметров или в процессе записи в базу данных.

Таким образом, в json.error мы найдем описание ошибки, формируемой на бекенде. Но это сработает в тех случаях, когда запрос до сервера дошел. Если же мы ошиблись, например, урлом или же сервер просто не отвечает, то в этом случае мы анализируем штатный ответ объекта xhr response.statusText.

Что делать с этой информацией об ошибке, решать Вам. Часто показывается какое-то ненавязчивое сообщение вроде "что-то пошло не так". Мы для примера напишем console.log("ERROR: ", message) и на этом закончим статью.

Итоги, демо и исходники

Надеюсь, я убедил Вас, что к промисам стоит присмотреться не только ради лишней строчки в резюме. Конечно, в статье мы и близко не рассмотрели все их возможности. По сути, мы затронули только частный случай - промисы, возвращаемые стандартным методом $.ajax, и мельком - $.when. Но зато убедились, что с этим можно работать и с их помощью писать простой и легко расширяемый код для довольно разнообразных и сложных задач.

Здесь можно рассмотренного кода. А тут посмотреть, . Просто нажимая на кнопки, Вы ничего не увидите. Открывайте консоль, вкладку Network и смотрите за порядком выполнения запросов, передаваемыми данными и возвращаемыми результатами. Убедитесь, что в нескольких последовательных запросах все параметры прокидываются правильно.

И конечно, делитесь собственными мыслями и соображениями в комментариях.

Многие начинают писать проект для работы с единственной задачей, не подразумевая, что это может вырасти в многопользовательскую систему управления, ну допустим, контентом или упаси бог, производством. И всё вроде здорово и классно, всё работает, пока не начинаешь понимать, что тот код, который написан - состоит целиком и полностью из костылей и хардкода. Код перемешанный с версткой, запросами и костылями, неподдающийся иногда даже прочтению. Возникает насущная проблема: при добавлении новых фич, приходится с этим кодом очень долго и долго возиться, вспоминая «а что же там такое написано то было?» и проклинать себя в прошлом.

Вы можеть быть даже слышали о шаблонах проектирования и даже листали эти прекрасные книги:

  • Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидесс «Приемы объектно ориентированного проектирования. Паттерны проектирования»;
  • М. Фаулер «Архитектура корпоративных программных приложений».
А многие, не испугавшись огромных руководств и документаций, пытались изучить какой-либо из современных фреймворков и столкнувшись со сложностью понимания (в силу наличия множества архитектруных концепций хитро увязанных между собой) отложили изучение и применение современных интсрументов в «долгий ящик».

Представленная статья будет полезна в первую очередь новичкам. Во всяком случае, я надеюсь что за пару часов вы сможете получить представление о реализации MVC паттерна, который лежит в основе всех современных веб-фреймворков, а также получить «пищу» для дальнейших размышлений над тем - «как стоит делать». В конце статьи приводится подборка полезных ссылок, которые также помогут разобраться из чего состоят веб-фреймворки (помимо MVC) и как они работают.

Прожженные PHP-программисты вряд ли найдут в данной статье что-то новое для себя, но их замечания и комментарии к основному тексту были бы очень кстати! Т.к. без теории практика невозможна, а без практики теория бесполезна, то сначала будет чуть-чуть теории, а потом перейдем к практике. Если вы уже знакомы с концепцией MVC, можете пропустить раздел с теорией и сразу перейти к практике.

1. Теория Шаблон MVC описывает простой способ построения структуры приложения, целью которого является отделение бизнес-логики от пользовательского интерфейса. В результате, приложение легче масштабируется, тестируется, сопровождается и конечно же реализуется.

Рассмотрим концептуальную схему шаблона MVC (на мой взгляд - это наиболее удачная схема из тех, что я видел):

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

Типичную последовательность работы MVC-приложения можно описать следующим образом:

  • При заходе пользователя на веб-ресурс, скрипт инициализации создает экземпляр приложения и запускает его на выполнение.
    При этом отображается вид, скажем главной страницы сайта.
  • Приложение получает запрос от пользователя и определяет запрошенные контроллер и действие. В случае главной страницы, выполняется действие по умолчанию (index ).
  • Приложение создает экземпляр контроллера и запускает метод действия,
    в котором, к примеру, содержаться вызовы модели, считывающие информацию из базы данных.
  • После этого, действие формирует представление с данными, полученными из модели и выводит результат пользователю.
  • Модель - содержит бизнес-логику приложения и включает методы выборки (это могут быть методы ORM), обработки (например, правила валидации) и предоставления конкретных данных, что зачастую делает ее очень толстой, что вполне нормально.
    Модель не должна напрямую взаимодействовать с пользователем. Все переменные, относящиеся к запросу пользователя должны обрабатываться в контроллере.
    Модель не должна генерировать HTML или другой код отображения, который может изменяться в зависимости от нужд пользователя. Такой код должен обрабатываться в видах.
    Одна и та же модель, например: модель аутентификации пользователей может использоваться как в пользовательской, так и в административной части приложения. В таком случае можно вынести общий код в отдельный класс и наследоваться от него, определяя в наследниках специфичные для подприложений методы.

    Вид - используется для задания внешнего отображения данных, полученных из контроллера и модели.
    Виды cодержат HTML-разметку и небольшие вставки PHP-кода для обхода, форматирования и отображения данных.
    Не должны напрямую обращаться к базе данных. Этим должны заниматься модели.
    Не должны работать с данными, полученными из запроса пользователя. Эту задачу должен выполнять контроллер.
    Может напрямую обращаться к свойствам и методам контроллера или моделей, для получения готовых к выводу данных.
    Виды обычно разделяют на общий шаблон, содержащий разметку, общую для всех страниц (например, шапку и подвал) и части шаблона, которые используют для отображения данных выводимых из модели или отображения форм ввода данных.

    Контроллер - связующее звено, соединяющее модели, виды и другие компоненты в рабочее приложение. Контроллер отвечает за обработку запросов пользователя. Контроллер не должен содержать SQL-запросов. Их лучше держать в моделях. Контроллер не должен содержать HTML и другой разметки. Её стоит выносить в виды.
    В хорошо спроектированном MVC-приложении контроллеры обычно очень тонкие и содержат только несколько десятков строк кода. Чего, не скажешь о Stupid Fat Controllers (SFC) в CMS Joomla. Логика контроллера довольно типична и большая ее часть выносится в базовые классы.
    Модели, наоборот, очень толстые и содержат большую часть кода, связанную с обработкой данных, т.к. структура данных и бизнес-логика, содержащаяся в них, обычно довольно специфична для конкретного приложения.

    1.1. Front Controller и Page Controller В большинстве случае, взаимодействие пользователя с web-приложением проходит посредством переходов по ссылкам. Посмотрите сейчас на адресную строку браузера - по этой ссылке вы получили данный текст. По другим ссылкам, например, находящимся справа на этой странице, вы получите другое содержимое. Таким образом, ссылка представляет конкретную команду web-приложению.

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

    Рассмотрим два варианта адресной строки, по которым показывается какой-то текст и профиль пользователя.

    Приблизительный код обработки в таком случае:
    switch($_GET["action"]) { case "about" : require_once("about.php"); // страница "О Нас" break; case "contacts" : require_once("contacts.php"); // страница "Контакты" break; case "feedback" : require_once("feedback.php"); // страница "Обратная связь" break; default: require_once("page404.php"); // страница "404" break; }
    Думаю, почти все так раньше делали.

    С использованием движка маршрутизации URL вы сможете для отображения той же информации настроить приложение на прием таких запросов:
    http://www.example.com/contacts/feedback

    Здесь contacts представляет собой контроллер, а feedback - это метод контроллера contacts, отображающий форму обратной связи и т.д. Мы еще вернемся к этому вопросу в практической части.

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

    2. Практика Для начала создадим следующую структуру файлов и папок:


    Забегая вперед, скажу, что в папке core будут храниться базовые классы Model, View и Controller.
    Их потомки будут храниться в директориях controllers, models и views. Файл index.php это точка в хода в приложение. Файл bootstrap.php инициирует загрузку приложения, подключая все необходимые модули и пр.

    Будем идти последовательно; откроем файл index.php и наполним его следующим кодом:
    ini_set("display_errors", 1); require_once "application/bootstrap.php";
    Тут вопросов возникнуть не должно.

    Следом, сразу же перейдем к фалу bootstrap.php :
    require_once "core/model.php"; require_once "core/view.php"; require_once "core/controller.php"; require_once "core/route.php"; Route::start(); // запускаем маршрутизатор
    Первые три строки будут подключать пока что несуществующие файлы ядра. Последние строки подключают файл с классом маршрутизатора и запускают его на выполнение вызовом статического метода start.

    2.1. Реализация маршрутизатора URL Пока что отклонимся от реализации паттерна MVC и займемся мрашрутизацией. Первый шаг, который нам нужно сделать, записать следующий код в .htaccess :
    RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule .* index.php [L]
    Этот код перенаправит обработку всех страниц на index.php , что нам и нужно. Помните в первой части мы говорили о Front Controller?!

    Маршрутизацию мы поместим в отдельный файл route.php в директорию core. В этом файле опишем класс Route, который будет запускать методы контроллеров, которые в свою очередь будут генерировать вид страниц.

    Содержимое файла route.php

    class Route { static function start() { // контроллер и действие по умолчанию $controller_name = "Main"; $action_name = "index"; $routes = explode("/", $_SERVER["REQUEST_URI"]); // получаем имя контроллера if (!empty($routes)) { $controller_name = $routes; } // получаем имя экшена if (!empty($routes)) { $action_name = $routes; } // добавляем префиксы $model_name = "Model_".$controller_name; $controller_name = "Controller_".$controller_name; $action_name = "action_".$action_name; // подцепляем файл с классом модели (файла модели может и не быть) $model_file = strtolower($model_name).".php"; $model_path = "application/models/".$model_file; if(file_exists($model_path)) { include "application/models/".$model_file; } // подцепляем файл с классом контроллера $controller_file = strtolower($controller_name).".php"; $controller_path = "application/controllers/".$controller_file; if(file_exists($controller_path)) { include "application/controllers/".$controller_file; } else { /* правильно было бы кинуть здесь исключение, но для упрощения сразу сделаем редирект на страницу 404 */ Route::ErrorPage404(); } // создаем контроллер $controller = new $controller_name; $action = $action_name; if(method_exists($controller, $action)) { // вызываем действие контроллера $controller->$action(); } else { // здесь также разумнее было бы кинуть исключение Route::ErrorPage404(); } } function ErrorPage404() { $host = "http://".$_SERVER["HTTP_HOST"]."/"; header("HTTP/1.1 404 Not Found"); header("Status: 404 Not Found"); header("Location:".$host."404"); } }


    Замечу, что в классе реализована очень упрощенная логика (несмотря на объемный код) и возможно даже имеет проблемы безопасности. Это было сделано намерено, т.к. написание полноценного класса маршрутизации заслуживает как минимум отдельной статьи. Рассмотрим основные моменты…

    В элементе глобального массива $_SERVER["REQUEST_URI"] содержится полный адрес по которому обратился пользователь.
    Например: example.ru/contacts/feedback

    С помощью функции explode производится разделение адреса на составлющие. В результате мы получаем имя контроллера, для приведенного примера, это контроллер contacts и имя действия, в нашем случае - feedback .

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

    Таким образом, при переходе, к примеру, по адресу:
    example.com/portfolio
    или
    example.com/portfolio/index
    роутер выполнит следующие действия:

  • подключит файл model_portfolio.php из папки models, содержащий класс Model_Portfolio;
  • подключит файл controller_portfolio.php из папки controllers, содержащий класс Controller_Portfolio;
  • создаст экземпляр класса Controller_Portfolio и вызовет действие по умолчанию - action_index, описанное в нем.
  • Если пользователь попытается обратиться по адресу несуществующего контроллера, к примеру:
    example.com/ufo
    то его перебросит на страницу «404»:
    example.com/404
    То же самое произойдет если пользователь обратится к действию, которое не описано в контроллере.2.2. Возвращаемся к реализации MVC Перейдем в папку core и добавим к файлу route.php еще три файла: model.php, view.php и controller.php


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

    Содержимое файла model.php
    class Model { public function get_data() { } }
    Класс модели содержит единственный пустой метод выборки данных, который будет перекрываться в классах потомках. Когда мы будем создавать классы потомки все станет понятней.

    Содержимое файла view.php
    class View { //public $template_view; // здесь можно указать общий вид по умолчанию. function generate($content_view, $template_view, $data = null) { /* if(is_array($data)) { // преобразуем элементы массива в переменные extract($data); } */ include "application/views/".$template_view; } }
    Не трудно догадаться, что метод generate предназначен для формирования вида. В него передаются следующие параметры:

  • $content_file - виды отображающие контент страниц;
  • $template_file - общий для всех страниц шаблон;
  • $data - массив, содержащий элементы контента страницы. Обычно заполняется в модели.
  • Функцией include динамически подключается общий шаблон (вид), внутри которого будет встраиваться вид
    для отображения контента конкретной страницы.

    В нашем случае общий шаблон будет содержать header, menu, sidebar и footer, а контент страниц будет содержаться в отдельном виде. Опять же это сделано для упрощения.

    Содержимое файла controller.php
    class Controller { public $model; public $view; function __construct() { $this->view = new View(); } function action_index() { } }
    Метод action_index - это действие, вызываемое по умолчанию, его мы перекроем при реализации классов потомков.

    2.3. Реализация классов потомков Model и Controller, создание View"s Теперь начинается самое интересное! Наш сайт-визитка будет состоять из следущих страниц:
  • Главная
  • Услуги
  • Портфолио
  • Контакты
  • А также - страница «404»
  • Для каждой из страниц имеется свой контроллер из папки controllers и вид из папки views. Некоторые страницы могут использовать модель или модели из папки models.


    На предыдущем рисунке отдельно выделен файл template_view.php - это шаблон, содержащий общую для всех страниц разметку. В простейшем случае он мог бы выглядеть так:
    Главная
    Для придания сайту презентабельного вида сверстаем CSS шаблон и интегририруем его в наш сайт путем изменения структуры HTML-разметки и подключения CSS и JavaScript файлов:

    В конце статьи, в разделе «Результат», приводится ссылка на GitHub-репозиторий с проектом, в котором проделаны действия по интеграции простенького шаблона.

    2.3.1. Создадаем главную страницу Начнем с контроллера controller_main.php , вот его код:
    class Controller_Main extends Controller { function action_index() { $this->view->generate("main_view.php", "template_view.php"); } }
    В метод generate экземпляра класса View передаются имена файлов общего шаблона и вида c контентом страницы.
    Помимо индексного действия в контроллере конечно же могут содержаться и другие действия.

    Файл с общим видом мы рассмотрели ранее. Рассмотрим файл контента main_view.php :
    Добро пожаловать!

    ОЛОЛОША TEAM - команда первоклассных специалистов в области разработки веб-сайтов с многолетним опытом коллекционирования мексиканских масок, бронзовых и каменных статуй из Индии и Цейлона, барельефов и изваяний, созданных мастерами Экваториальной Африки пять-шесть веков назад...


    Здесь содержиться простая разметка без каких либо PHP-вызовов.
    Для отображения главной странички можно воспользоваться одним из следующих адресов:

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

    2.3.2. Создадаем страницу «Портфолио» В нашем случае, страница «Портфолио» - это единственная страница использующая модель.
    Модель обычно включает методы выборки данных, например:
  • методы нативных библиотек pgsql или mysql;
  • методы библиотек, реализующих абстракицю данных. Например, методы библиотеки PEAR MDB2;
  • методы ORM;
  • методы для работы с NoSQL;
  • и др.
  • Для простоты, здесь мы не будем использовать SQL-запросы или ORM-операторы. Вместо этого мы сэмулируем реальные данные и сразу возвратим массив результатов.
    Файл модели model_portfolio.php поместим в папку models. Вот его содержимое:
    class Model_Portfolio extends Model { public function get_data() { return array(array("Year" => "2012", "Site" => "http://DunkelBeer.ru", "Description" => "Промо-сайт темного пива Dunkel от немецкого производителя Löwenbraü выпускаемого в России пивоваренной компанией "CАН ИнБев"."), array("Year" => "2012", "Site" => "http://ZopoMobile.ru", "Description" => "Русскоязычный каталог китайских телефонов компании Zopo на базе Android OS и аксессуаров к ним."), // todo); } }

    Класс контроллера модели содержится в файле controller_portfolio.php , вот его код:
    class Controller_Portfolio extends Controller { function __construct() { $this->model = new Model_Portfolio(); $this->view = new View(); } function action_index() { $data = $this->model->get_data(); $this->view->generate("portfolio_view.php", "template_view.php", $data); } }
    В переменную data записывается массив, возвращаемый методом get_data , который мы рассматривали ранее.
    Далее эта переменная передается в качестве параметра метода generate , в который также передаются: имя файла с общим шаблон и имя файла, содержащего вид c контентом страницы.

    Вид содержащий контент страницы находится в файле portfolio_view.php .
    Портфолио

    Все проекты в следующей таблице являются вымышленными, поэтому даже не пытайтесь перейти по приведенным ссылкам.
    ГодПроектОписание


    Здесь все просто, вид отображает данные полученные из модели.

    2.3.3. Создаем остальные страницы Остальные страницы создаются аналогично. Их код досутпен в репозитории на GitHub, ссылка на который приводится в конце статьи, в разделе «Результат».3. Результат А вот что получилось в итоге:

    Скриншот получившегося сайта-визитки



    Ссылка на GitHub: https://github.com/vitalyswipe/tinymvc/zipball/v0.1

    А вот в этой версии я набросал следующие классы (и соответствующие им виды):

    • Controller_Login в котором генерируется вид с формой для ввода логина и пароля, после заполнения которой производится процедура аутентификации и в случае успеха пользователь перенаправляется в админку.
    • Contorller_Admin с индексным действием, в котором проверяется был ли пользователь ранее авторизован на сайте как администратор (если был, то отображается вид админки) и действием logout для разлогинивания.
    Аутентификация и авторизация - это другая тема, поэтому здесь она не рассматривается, а лишь приводится ссылка указанная выше, чтобы было от чего оттолкнуться.4. Заключение Шаблон MVC используется в качестве архитектурной основы во многих фреймворках и CMS, которые создавались для того, чтобы иметь возможность разрабатывать качественно более сложные решения за более короткий срок. Это стало возможным благодаря повышению уровня абстракции, поскольку есть предел сложности конструкций, которыми может оперировать человеческий мозг.

    Но, использование веб-фреймворков, типа Yii или Kohana, состоящих из нескольких сотен файлов, при разработке простых веб-приложений (например, сайтов-визиткок) не всегда целесообразно. Теперь мы умеем создавать красивую MVC модель, чтобы не перемешивать Php, Html, CSS и JavaScript код в одном файле.

    Данная статья является скорее отправной точкой для изучения CMF, чем примером чего-то истинно правильного, что можно взять за основу своего веб-приложения. Возможно она даже вдохновила Вас и вы уже подумываете написать свой микрофреймворк или CMS, основанные на MVC. Но, прежде чем изобретать очередной велосипед с «блекджеком и шлюхами», еще раз подумайте, может ваши усилия разумнее направить на развитие и в помощь сообществу уже существующего проекта?!

    P.S.: Статья была переписана с учетом некоторых замечаний, оставленных в комментариях. Критика оказалась очень полезной. Судя по отклику: комментариям, обращениям в личку и количеству юзеров добавивших пост в избранное затея написать этот пост оказалось не такой уж плохой. К сожалению, не возможно учесть все пожелания и написать больше и подробнее по причине нехватки времени… но возможно это сделают те таинственные личности, кто минусовал первоначальный вариант. Удачи в проектах!

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

    Теги: Добавить метки

    In PHP, the predefined $ _POST variable is used to form from the method = "post" in the value of the collection.

    $ _POST Variable

    Forms predefined $ _POST variable is used to collect from the method = "post" in value.

    Information form sent with the POST method from, for anyone not visible (not displayed in the browser"s address bar), and on the amount of information sent is also not limited.

    Note: However, by default, the maximum amount of information sent to the POST method 8 MB (can be changed by setting the php.ini file post_max_size).

    Examples

    form.html file code is as follows:

    本教程(сайт) 名字: 年龄:

    When the user clicks the "Submit" button, URL similar to the following:

    Http://www..php

    "Welcome.php" file can now collect $ _POST variable to the form data (Note that the name of the form fields will automatically become $ _POST array keys):

    欢迎 !
    你的年龄是 岁。

    Demo accessed through a browser as follows:

    When to use method = "post"?

    Information from a form with the POST method of transmission, are not visible to anyone, and on the amount of information sent is also not limited.

    However, since the tag is not displayed in the URL, it is not possible to bookmark this page.

    PHP $ _REQUEST variable

    Predefined $ _REQUEST variable contains the $ _GET, $ _ POST and content of the $ _COOKIE.

    $ _REQUEST Variable can be used to collect form data sent via GET and POST methods.

    Examples

    You can "welcome.php" file is modified as follows the code, it can accept $ _GET, $ _ POST and other data.

    欢迎 !
    你的年龄是 岁。

    Я всего лишь хотел дернуть данные с сайта за авторизацией и что получилось.

    file_get_contents

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

    Вот таким одностраничником можно загрузить данные POST запросом:

    $result = file_get_contents($url, false, stream_context_create(array(‘http’ => array(‘method’ => ‘POST’,’header’ => ‘Content-Type: application/xml’,’content’ => $xml))));

    Отформатированный вариант:

    pear:http_request

    Уроццкий метод, просто бесит смотреть в такую парашу. Конечно я бы мог причесать этот взятый из отсюда код: http://pear.php.net/manual/pl/package.http.http-request.intro.php

    Но и так видно, работает, но выглядит как отстой.

    Pecl_Http

    Что это за хрень — какое-то странное расширение PHP, для которого я примеров-то найти не смог, зато на php.net ГИГАНСКИЙ МАНУЛИЩЩЕ.

    Впрочем нашел кое-что поглядеть, надеюсь сами дадите примеров еще:

    $request = Request::factory($url); $request->method("POST"); $request->headers($header); $request->post($post_params); $request->execute(); Curl

    Самый мировой метод, который используется в 90% случаев, но говорят и то не всегда работает.

    if($curl = curl_init()) {

    curl_setopt($curl, CURLOPT_URL, ‘http://mysite.ru/receiver.php’);

    curl_setopt($curl, CURLOPT_RETURNTRANSFER,true);

    curl_setopt($curl, CURLOPT_POST, true);

    curl_setopt($curl, CURLOPT_POSTFIELDS, «a=4&b=7»);

    $out = curl_exec($curl);

    curl_close($curl);

    Код CURL безобразен как моя задница, что бы ни говорили.

    Сокеты

    Сразу задавлю примером:

    $fp = fsockopen($url, 80, $errno, $errstr, 30);

    echo «ERROR: $errstr ($errno)
    \n»;

    $error=»ERROR: $errstr ($errno)»;

    $out = «GET / HTTP/1.1\r\n»;

    $out .=»User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1284.0 Safari/537.13\r\n»;
    $out .= «Host: $url\r\n»;

    $out .= «Referer: http://сайт»;

    $out .= «Connection: Close\r\n\r\n»;

    $out .= «\r\n»;
    fwrite($fp, $out);

    while (!feof($fp)) {

    $src.=fgets($fp, 128);

    Это пример из жизни, Уродливее некуда, зато работает везде и всегда, никаких библиотек не надо, прямо слезы на глазах. Даже PHP4 хватит.

    Snoopy

    Говорят чем-то покруче чем Snoopy. Не првоерял, код смотрел, документации нет. Интересно но времени жалко.

    Zend_http_client/Zend:http

    Документация есть даже на Русском кое что. Большой, всемогущий, скучный.

    Buzz – Simple HTTP Request Library

    Buzz еще одна библиотечка для HTTP запросов. Вот пример кода:

    1 $request = new Buzz\Message\Request("HEAD" , "/" , "http://google.com");
    2 $response = new Buzz\Message\Response();
    3
    4 $client = new Buzz\Client\FileGetContents();
    5 $client ->send($request , $response);
    6
    7 echo $request ;
    8 echo $response ;

    Не смотрел еще и не разбирался, но вы обязательно поглядите.

    Requests – Easy HTTP Requests

    Requests библиотека позволит делать легко HTTP запросы. Если вы (как и я) никак не можете запомнить синтаксис Curl,то эта библиотека для вас, пример кода.

    Похожие статьи

    • Три способа подключения «Мобильного банка» от Сбербанка

      Мобильный банк — такой же важный инструмент управления своими деньгами, как и сервис Сбербанк онлайн. Тем более, второй без первого практически не работает! Пакет «Эконом» берет с клиента 15 рублей за проверку последней операции на карте и...

    • Что такое яндекс директ и как он работает

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

    • Управление ставками Яндекс

      В этой статье вы узнаете, что такое Яндекс Директ и как он работает в интернете. Также я дам советы и кейсы, которые помогут вам повысить эффективность и увеличить прибыль от контекстной рекламы. Для владельцев сайтов — это...

    • Планшет самсунг стал долго заряжаться

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

    • LTE — что это такое в телефоне, как пользоваться Какой лучше 4g или lte

      В Украине скоро заработает связь нового поколения. Мы уже , как определить совместимость смартфона с 4G, а также о семи нюансах работы технологии в Украине. Теперь редакция объясняет, в чем разница между 4G и LTE. #1. Что такое 4G? 4G -...

    • Можно ли заряжать телефон (смартфон) от компьютера через USB-порт?

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