Когда-то давно веб-приложения были монолитами, выполняющими все самостоятельно. Сегодня многое поменялось. Появились многочисленные веб-сервисы со своими API, которые позволяют разработчикам обрабатывать разные функциональные участки – к примеру, аутентификацию или отправку писем пользователям.
Самый распространенный случай – использование API для подключения к одному из популярных онлайн-сервисов (Google, Twitter и т.д.). Однако иногда вы можете столкнуться с ситуацией, когда вам нужно подключиться к своему же приложению.
Именно с этим я столкнулся несколько дней назад: я создал произвольную корзину товаров в виде приложения на Ruby on Rails, и некоторое время радостно использовал ее на простеньком HTML-лендинге. Однако через некоторое время я обнаружил, что лендинг нужно сделать более привлекательным для посетителей, поэтому решил его обновить. Мне хотелось сделать блог, поэтому я остановил свой выбор на WordPress.
С созданным сайтом на WordPress все было хорошо, за исключением одной маленькой детали: когда посетители добавляли товары в корзину, и затем возвращались на сайт, чтобы совершить другие покупки, они никак не могли просмотреть товары, которые уже были в корзине. Корзину товаров и сайт нужно было как-то связать друг с другом. Мне нужно было сделать так, чтобы контент корзины выводился на основном сайте.
Для этого мне пришлось разработать API.
Создав API для корзины товаров и затем вызвав его из моей темы WordPress, я смог бы передать данные элегантным способом, не ломая всю систему.
В этом учебном руководстве, на основе уроков, которые я извлек для себя, работая над проектом, я рассмотрю обе стороны взаимодействия с API: во-первых, с помощью gem Grape мы создадим API, чтобы открыть для внешних приложений корзину товаров, базирующуюся на Ruby on Rails. Во-вторых, мы будем использовать API, чтобы получить контент корзины товаров и вывести его на WordPress-сайте. В зависимости от ваших требований, вас может заинтересовать либо создание всего API целиком, либо только вызов внешнего API из WordPress. Так или иначе, я надеюсь, что вы найдете данное руководство полезным и интересным.
Чтобы выполнить код в этом руководстве, вам нужно будет установить следующее:
- Ruby on Rails версии 3.2
- PHP 5.3
- cURL и его PHP / Apache библиотеки
Данное руководство, несмотря на свою относительную простоту, требует базового понимания Ruby, Ruby on Rails, PHP и WordPress.
Шаг 1. Используем приложение для примера
Так как вы планируете подключить Ruby on Rails приложение к WordPress-сайту, можно предположить, что у вас уже имеется готовое Rails приложение – и может даже WordPress-сайт. Таким образом, я создал для данного руководства простую, но при этом функциональную версию корзины товаров, обладающую методами для основных действий, которые требуются на сайте.
Вы можете скачать код примера или просто прочитать руководство и применить его к вашему собственному проекту Rails.
Проект Ruby on Rails, выбранный в качестве примера, представляет собой базовый скелет, и состоит из простой модели (ShoppingCart) и контроллера (ShoppingCartController). В модели ShoppingCart вы обнаружите следующие методы:
- find_or_create_with(token): этот статичный метод ищет и возвращает объект (корзину товаров) с предоставленным идентификационным токеном. Если такой объект не найден (или токен не указан), создается и возвращается новая корзина товаров.
- token: метод возвращает токен, идентифицирующий текущий объект – корзину товаров.
- as_json: метод возвращает контент корзины товаров в виде JSON-форматированной строки.
Контроллер ShoppingCartController содержит в себе действие для добавления товаров к корзине. Действие add принимает два параметра: token и id (идентификатор товара).
http://<SERVER_PATH>/shopping_cart/add/<id>?token=<token>
Мы вернемся к ним позже, а теперь давайте перейдем к созданию API для корзины товаров.
Шаг 2. Устанавливаем Grape и создаем API
Теперь, когда проект Rails у нас имеется, давайте создадим API для него.
Чтобы создать API, мы будем использовать инструмент под названием Grape. Grape – это микро-фреймворк, разработанный для реализации простых, REST-подобных API; требуется он в тех ситуациях, «когда вам не требуется мощь крупных фреймворков, таких как Rails», как говорят разработчики.
Фреймворк Grape может использоваться отдельно, однако он также применяется и в связке с Rails, предлагая простой способ создания API для доступа к подмножеству (или ко всей) функциональности вашего Rails приложения. Что как раз нам и понадобится сделать в нашем руководстве.
Устанавливаем Grape
Чтобы добавить gem к вашему Rails проекту, вводим следующую строку в ваш Gemfile:
gem 'grape'
Затем обновляем ваши gems с помощью следующего вызова (в командной строке):
bundle install
Готово. Gem был установлен, и вы можете использовать его.
Настраиваем пути Grape
Чтобы приступить к написанию API, вам сначала нужно создать исходный файл ruby, который будет содержать в себе API функциональность. Это легкий фреймворк, поэтому мы можем записать весь API в один файл. Поместите этот файл в свое Rails приложение в каталог api, который должен находиться в главной директории app.
В нашем примерном проекте вы найдете API код в файле app/api/cart_external.rb.
Добавьте следующие строки к вашему конфигурационному файлу application.rb, чтобы убедиться в том, что файлы из app/api будут подключены:
config.paths.add "app/api", glob: "**/*.rb" config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
Создаем модуль API
В исходном файле API нам нужно определить модуль, который будет содержать наш API класс:
module CartExternal class API < Grape::API version 'v1', :using => :path format :json helpers do end resource :cart do end end end
Здесь представлена базовая версия контента простого Grape API. В данный момент код ничего не делает; давайте рассмотрим конфигурационные элементы в самом начале кода в классе.
- Сначала строка version ‘v1’, :using => :path сообщает, что это версия v1 API, и что версия API, к которой пользователь хочет получить доступ, должна быть определена в URL – к примеру, http://localhost:3000/v1/. Другие опции для этого конфигурационного поля — :header, :accept_version_header и :param, каждая из них определяет свой собственный метод передачи информации о версии. Изучите документацию Grape для подробной информации об использовании каждой опции.
- format :json указывает Grape на то, что исходящие ответы нужно представить в виде JSON-строк (также можно использовать :txt и :xml).
Монтируем API
Перед тем, как мы начнем добавлять функциональность к API, нам нужно объявить API класс для нашего Rails приложения.
mount CartExternal::API => '/'
Давайте добавим следующую строку к файлу config/routes.rb:
Шаг 3. Заставляем API работать.
Теперь давайте вернемся к классу CartExternal::API, который мы задали выше.
Определяем ресурс
В API классе вы найдете два элемента, про которые я еще не упоминал: helpers и resource.
- helpers – это макрос, который может использоваться вместе с блоком (или модулем) для предоставления вспомогательных методов, используемых в действиях API. Эти методы могут потребоваться для верификации API-параметров или для взаимодействия с родительским Rails приложением.
- resource – определяет логический набор API-методов. Вы можете добавлять их столько, сколько захотите, но в нашем случае мы обойдемся одним, который мы назовем :cart. Этот ресурс будет содержать все API методы, связанные с корзиной товаров. Благодаря этим определениям, любые вызовы, сделанные к http://<APPLICATION_PATH>/v1/cart, будут направлены к нашему ресурсу и действиям, определенным в нем.
Создаем свое первое API действие
Чтобы понять, как это работает, давайте создадим API вызов для получения элементов в корзине товаров, идентифицированной по токену.
В блок resources добавляем следующий код:
desc 'Return items in cart.' params do optional :token, type: String, default: nil end get '/items' do token = params[:token] cart = ShoppingCart.find_or_create_with token response = { status: 200, token: cart.token, cart: cart.as_json } response.as_json end
Теперь у нас есть действие, которое отвечает за то, чтобы GET-запросы пересылались к /items.
Действие принимает один необязательный строковый параметр, token, описанный в блоке params. Объявлять параметры не обязательно, однако это хорошая практика, позволяющая сохранить ваш код читабельным и сэкономить ваше время, поскольку Grape выполняет большую часть валидации параметров за вас. Для обязательных параметров вы можете использовать похожую конструкцию, меняя optional на requires. Обязательные параметры не могут иметь дефолтное значение.
Наше действие ищет корзину товаров, которая отвечает токену, или создает новую, если токен не был передан. Затем возвращается JSON-ответ, который содержит статус, токен актуальной корзины товаров, а также контент корзины.
Тестируем API действие
Если вы запустили свое Rails приложение локально, вы можете теперь включить свой сервер и передать браузеру http://localhost:3000/v1/cart/items, чтобы увидеть ответ, который должен выглядеть следующим образом:
{"status":200,"token":"XBrf2nCkTYfQJoU8d4A1nw","cart":{"item_count":0,"items":[]}}
Поскольку мы не передавали токен в нашем вызове, будет создана новая корзина товаров, и к ней будет привязан случайным образом сгенерированный уникальный токен. Именно поэтому корзина пока остается пуста.
Если вы хотите, вы можете для тестирования, к примеру, использовать действие, добавленное к ShoppingCartController для внесения товара в корзину:
http://localhost:3000/shopping_cart/add/1?token=TOKEN_FROM_PREVIOUS_CALL
После добавления товара, если вы снова вызовете API, вы увидите, что теперь товар появился в корзине:
{"status":200,"token":"XBrf2nCkTYfQJoU8d4A1nw","cart":{"item_count":1,"items":[{"cart_id":2,"created_at":"2014-05-20T16:44:24Z","id":4,"product_id":1,"product_name":"Test Product","product_price":"10.0","updated_at":"2014-05-20T16:44:24Z"}]}}
Внесите столько пунктов, сколько вам хочется; теперь, прежде чем добавлять расширенную функциональность, давайте завершим установку и заставим WordPress обратиться к API.
Шаг 4. Вызываем API из WordPress
Мы уже протестировали API, вызвав его из браузера, и теперь пришла пора заставить WordPress связаться с API.
Код, представленный ниже, может быть добавлен как в плагин, так и в файл темы functions.php, что зависит от вашего проекта и контекста того, где именно требуется взаимодействие с API. В моем случае для интеграции корзины товаров я решил добавить код в дочернюю тему.
Какой бы путь вы ни выбрали, код будет одним и тем же.
Создаем функцию для вызова API
Открываем файл functions.php (или соответствующий PHP-файл плагина) и вставляем в него следующую PHP-функцию. Это вспомогательная функция, которая инкапсулирует API-взаимодействие, чтобы в будущем, если вам понадобится изменить сигнатуру API или другие универсальные элементы в вызовах, вы могли это сделать в одном месте.
Вот что должно быть в этой функции:
function api_get_as_json( $action, $params ) { $api_endpoint = "http://localhost:3000/v1/cart"; if ( null == $params ) { $params = array(); } // Create URL with params $url = $api_endpoint . $action . '?' . http_build_query($params); // Use curl to make the query $ch = curl_init(); curl_setopt_array( $ch, array( CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true ) ); $output = curl_exec($ch); // Decode output into an array $json_data = json_decode( $output, true ); curl_close( $ch ); return $json_data; }
Функция получает путь к API действию и обязательные параметры, объединяя их в URL и затем используя cURL, чтобы выполнить запрос к API.
После получения ответа функция передает его в массив, используя json_decode – функцию, которая является частью стандартных PHP-библиотек.
Вызываем API и обрабатываем ответ
Теперь, когда мы создали функциональность для вызова API, давайте используем ее, чтобы запросить контент корзины товаров через наш API-метод /cart/items, и затем уже выведем ответ:
function show_shopping_cart() { $cart_data = api_get_as_json('/items', null); ?> <div class="shopping-cart"> <h3>Shopping Cart</h3> <ul> <?php if (isset($cart_data["cart"]["items"]) && $cart_data["cart"]["items"].length > 0) : ?> <?php foreach ($cart_data["cart"]["items"] as $item) : ?> <li> <?php echo $item["product_name"]; ?> $<?php echo $item["product_price"]; ?> </li> <?php endforeach; ?> <?php else : ?> Your shopping cart is empty. <?php endif; ?> </ul> </div> <div style="margin-top: 20px;"> <pre style="font-size: 8pt;"><?php print_r($cart_data); ?></pre> </div> <?php }
Функция совершает вызов API к /items и выводит ответ с некоторым базовым HTML-форматированием. В самом конце функции я добавил простой print_r блок, который обрабатывает контент JSON-ответа (это полезно для отладки).
Это полезно в самом начале, когда корзина еще пуста, и мы хотим убедиться, что нет никаких ошибок соединения, которые могут препятствовать выводу ее контента.
Подцепляем функцию к WordPress
Теперь, когда у нас имеются функции для выполнения вызова и печати ответа, нам нужно, чтобы WordPress обращался к ним.
Самый простой способ это сделать – перейти к шаблонам темы и вызвать show_shopping_cart там, где вам нужна корзина. Если тему разрабатывали вы сами, и вам больше ничего не понадобится настраивать, вы можете легко так поступить.
С другой стороны, вы можете воспользоваться самым гибким и дружественным к пользователям подходом – встроить вызов в виджет WordPress. Таким образом, вы сможете поместить корзину товаров в любой сайдбар на вашем сайте. Это не трудно сделать, поэтому давайте поступим именно так.
Все, что нам нужно – это подкласс WP_Widget с конструктором и некоторым кодом для обработки контента корзины в функции widget. Две других функции, form и options, могут быть оставлены пустыми в данный момент (если вы хотите несколько изменить виджет, вы можете воспользоваться этими функциями).
class ShoppingCartWidget extends WP_Widget { public function __construct() { parent::__construct( 'shopping_cart_widget', __( 'Shopping Cart Widget', 'shopping_cart' ), array( 'description' => __( 'A widget for displaying a shopping cart.', 'shopping_cart' ) ) ); } /** * Outputs the content of the widget */ public function widget( $args, $instance ) { echo $args['before_widget']; show_shopping_cart(); echo $args['after_widget']; } /** * Outputs the options form. */ public function form( $instance ) { // The widget takes no options } /** * Processing widget options on save */ public function update( $new_instance, $old_instance ) { // The widget takes no options } } function register_shopping_cart_widget() { register_widget( 'ShoppingCartWidget' ); } add_action('widgets_init', 'register_shopping_cart_widget');
Теперь переходим в админку WordPress и перетаскиваем виджет Shopping Cart Widget в один из наших сайдбаров.
Переходим на сайт и видим код в действии. Вы должны увидеть что-то подобное:
Ваша корзина товаров по-прежнему пуста, однако, как показывает вывод print_r, соединение работает, и WordPress-блог получает данные из нашего Rails приложения.
Теперь, когда все основы нами рассмотрены, давайте взглянем на то, как сделать наше соединение более защищенным.
Защищаем соединение с помощью API-ключа
Мы создали API для Ruby on Rails приложения и вызвали его с сайта WordPress; однако у нас по-прежнему остались важные бреши – нам нужно защитить API, чтобы оно не было открыто для запросов других людей в сети. Я думаю, вы понимаете, что может произойти с вашей квартирой, если вы оставите дверь открытой.
Самый простой способ защитить API – это генерировать секретный ключ для каждого приложения, которому позволено вызывать ваш API. Таким образом, каждый раз, когда приложение выполняет запрос к API, оно должно отправить свой API-ключ серверу для проверки. Вызывающей стороне без валидного ключа будет запрещено делать запросы. Для дополнительной защиты большинство сервисов включают секретный код API в дополнение к API-ключу.
Таким образом, давайте добавим API-ключ – сначала к нашему Ruby-коду, а затем уже и к WordPress.
Добавляем метод для проверки API ключа
Для начала добавим метод для проверки API ключа. Он будет располагаться в блоке helpers нашего API класса:
def is_valid_api_key?(key) key == 'API_KEY' end
Это очень простая версия с одним API-ключом – вы всегда можете расширить ее в зависимости от своих потребностей. К примеру, если вы имеете многочисленные приложения, вызывающие API, вам понадобится задать уникальный API-ключ для каждого из них и хранить все эти ключи в базе данных. Вы можете также добавить страницу в админку для создания новых API-ключей.
По крайней мере, вам нужно заменить API_KEY на реальный сгенерированный ключ, который будет не так просто угадать. В этом примере нам важен только API-ключ и метод для его проверки, который будет устанавливать его соответствие с ключом, хранимым в системе.
Используем вспомогательный метод для проверки API-ключа
Воспользовавшись вспомогательным методом, мы можем добавить проверку к нашему единственному API-вызову, окружив код в нем следующей if…else конструкцией:
if is_valid_api_key? params[:key] token = params[:token] cart = ShoppingCart.find_or_create_with token response = { status: 200, token: cart.token, cart: cart.as_json } response.as_json else error!('401 Unauthorized', 401) end
Обратите внимание, что блок else, в котором мы обрабатываем ошибку, использует Grape метод error!.
Теперь давайте перезапустим сервер Rails и перезагрузим нашу главную страницу WordPress. Мы увидим следующее:
Все потому, что у нас пока еще не добавлен API ключ к нашему WordPress-коду, и Rails API больше не принимает запросы от WP. Если вы хотите, вы можете на данном этапе добавить некоторую обработку ошибок и вывести привлекательное сообщение об ошибке.
Идем дальше. Давайте сделаем так, чтобы API вызов заработал снова.
Добавляем поле параметров к API-ключу
На данный момент мы могли бы просто прописать фиксированный API-ключ в нашей функции api_get_as_json, однако добавление новых опций на страницу общих настроек WordPress – предпочтительный вариант для нас, тем более он не вызывает затруднений.
Также мы можем добавить опцию, задающую URL для API.
Таким образом, позже, когда вы развернете WordPress на живом сервере, вы сможете провести все настройки без касания кода – что очень хорошо и удобно.
$cart_api_settings_field = new CartApiSetting(); class CartApiSetting { function CartApiSetting() { add_filter( 'admin_init', array( $this , 'register_fields' ) ); } function register_fields() { register_setting( 'general', 'cart_api_key', 'esc_attr' ); add_settings_field( 'cart_api_key', '<label for="cart_api_key">' . __( 'Shopping Cart API key' , ‘shopping_cart’ ).'</label>' , array( $this, 'fields_html' ) , 'general' ); } function fields_html() { $value = get_option( 'cart_api_key', '' ); echo '<input type="text" id="cart_api_key" name="cart_api_key" value="' . $value . '" />'; } }
Вот определение поля параметров для URL, используя ту же самую конструкцию:
$cart_api_endpoint_settings_field = new CartApiEndpointSetting(); class CartApiEndpointSetting { function CartApiEndpointSetting() { add_filter( 'admin_init', array( $this, 'register_fields' ) ); } function register_fields() { register_setting( 'general', 'cart_api_endpoint', 'esc_attr' ); add_settings_field( 'cart_api_endpoint', '<label for="cart_api_endpoint">' . __( 'Shopping Cart API URL' , ‘shopping_cart’ ).'</label>' , array( $this, 'fields_html' ) , 'general' ); } function fields_html() { $value = get_option( 'cart_api_endpoint', '' ); echo '<input type="text" id="cart_api_endpoint" name="cart_api_endpoint" value="' . $value . '" />'; } }
Перейдите к меню Параметры в WordPress, чтобы задать верные значения для этих настроек; убедитесь в том, что API-ключ соответствует значению, которое вы ввели в вашем Rails-сервисе.
Передаем API-ключ с API-запросом
Теперь, когда API-ключ и URL хранятся в виде опций WordPress, давайте обновим функцию API-вызова.
Изменения затронут начало функции:
function api_get_as_json($action, $params) { if ( null == $params ) { $params = array(); } $api_key = get_option( "cart_api_key" ); $api_endpoint = get_option( "cart_api_endpoint" ); if ( ! isset( $api_key ) || ! isset( $api_endpoint ) ) { return false; } $params["key"] = $api_key; }
Взгляните на новый код выше. Вы обнаружите, что параметры $api_key и $api_endpoint считываются из опций WordPress. $api_endpoint уже использовался для создания URL вызова, однако для $api_key нам понадобилось добавить строку 14, чтобы включить значение в параметры, переданные в API.
Обновите вашу главную страницу WordPress, и контент корзины товаров снова появится на экране, но на сей раз уже защищенный с помощью API-ключа. Все выглядит прекрасно – так же, как и до запроса API-ключа – и вы соединяете Rails приложение с вашим WordPress-сайтом.
Шаг 5. Добавляем общую сессию (shared session).
Код WordPress, который мы написали ранее, совершает API вызовы и защищает их с помощью API-ключа, но делает все это без сохранения состояния. Для простых действий, не требующих сохранения состояния, как, к примеру, публикация обновлений на доске объявлений или на вашей собственной платформе микроблогов, одноразовые вызовы вполне подойдут. Однако для нашего примера с корзиной товаров этого будет недостаточно.
Как вы могли заметить при тестировании кода, когда наш Rails сервер идентифицирует корзину товаров через токен, мы не используем этот токен больше нигде, поэтому при выполнении каждого нового вызова к корзине сервер создает ее снова.
Это не очень хорошо. Чтобы использовать корзину по назначению, нам нужно сохранить добавленные к ней товары на какое-то время, даже если вы перешли с одной страницы на другую.
Одним словом, нам нужны сессии.
Есть несколько способов, которыми мы можем создать сессию, однако давайте сейчас воспользуемся самым простым из них, используя токен, переданный нам приложением Rails, и сохраняя его в клиентские cookie. Поскольку ваш WordPress-сайт – это интерфейс, который видит и с которым взаимодействует пользователь (и его веб-браузер), нам нужно также установить для него cookie.
Создаем cookie
Cookies могут быть заданы только в хэдере, еще до того, как любой контент будет представлен, поэтому нам нужно несколько изменить наш код, переместив вызов /cart/items в поток выполнения. Чтобы сделать это, давайте создадим новую PHP-функцию, get_shopping_cart, и вызовем ее сразу в самом начале выполнения сайта WordPress.
Сделать это можно с помощью хуков WordPress. Полный список хуков можно узнать в кодексе. В нашем случае понадобится хук init, к которому мы подцепим наш запрос.
add_action( "init", "get_shopping_cart" );
Функция get_shopping_cart выглядит следующим образом:
function get_shopping_cart() { $cart_data = api_get_as_json( '/items', null ); $token = $cart_data['token']; // Expire cookie in 30 minutes setcookie( 'bread_cart', $token, time() + 60 * 30 ); set_saved_cart_data( $cart_data ); }
Сначала функция совершает уже знакомый нам вызов API-действия /items. Затем мы имеем нечто новое: в строке 3 мы извлекаем токен корзины товаров, возвращенный API, и потом, через несколько строк, сохраняем его в cookie. Срок жизни cookie задан в 30 минут – за это время, как я думаю, человек успеет сделать покупку.
В строке 7 вы можете отметить новый для себя вызов функции: set_saved_cart_data. Мы вернемся к нему буквально через минуту. Но сначала давайте заставим вызовы API также использовать токены.
Используем токен, полученный из cookie
Сначала мы создаем вспомогательную функцию, которая получает токен из cookie:
function api_shopping_cart_token() { $token = null; if ( isset( $_COOKIE[‘shopping_cart_token’] ) ) { $token = $_COOKIE['shopping_cart_token']; } return $token; }
Затем мы добавляем следующую строку к api_get_as_json сразу после строки, где мы задавали API-ключ:
$params['token'] = api_shopping_cart_token();
Теперь всякий раз, когда будет сделан вызов API, метод api_get_as_json будет получать токен корзины товаров из cookie и добавлять его к параметрам запроса. Если cookie не будет найден, API передаст пустой токен, т.е. создастся новая, пустая корзина.
Оптимизируем выполнение при помощи сохранения некоторых данных
Теперь вернемся к set_saved_cart_data.
Как мы могли видеть в коде выше, get_shopping_cart получает весь контент корзины – практически те же самые данные, которые мы запрашивали в show_shopping_cart. Это означает, что мы вызываем API два раза при обработке одной и той же страницы WordPress, когда достаточно было бы одного вызова. Сохранение ответа на время HTTP запроса – это простой способ оптимизации, который мы можем использовать для снижения числа API-запросов наполовину.
В PHP глобальные переменные определены и существуют только для одного HTTP-запроса, поэтому мы можем безопасно использовать одну переменную для хранения данных get_shopping_cart, чтобы с ее помощью обработать контент корзины товаров.
Чтобы сделать это, я создал простую пару функций, set_saved_cart_data и get_saved_cart_data, которые обернуты в глобальную переменную $g_shopping_cart_data, чтобы сохранить код читабельным и простым в управлении:
global $g_shopping_cart_data; function set_saved_cart_data( $cart_data ) { global $g_shopping_cart_data; $g_shopping_cart_data = $cart_data; } function get_saved_cart_data() { global $g_shopping_cart_data; return $g_shopping_cart_data; }
Учитывая, что все данные корзины товаров хранятся в глобальной переменной, вы можете теперь просто изменить первую строку в show_shopping_cart на:
$cart_data = get_saved_cart_data();
Теперь корзина товаров имеет сессию, и пользователь может добавлять товары в корзину и перемещаться по страницам, видя ту же самую корзину товаров.
Тестируем добавление товаров в корзину
Чтобы протестировать функциональность, давайте создадим ссылку на действие контроллера shopping_cart/add и выведем его в самом конце show_shopping_cart. Обратите внимание, что контроллеру для создания общей сессии требуется токен корзины в качестве параметра:
$product_id = 1; $token_params = http_build_query(array('token' => api_shopping_cart_token())); $url = "http://localhost:3000/shopping_cart/add/" . $product_id . "?" . $token_params; echo '<p><a href="' . $url . '">Add test item to cart</a></p>';
Щелкаем по ссылке Add test item to cart, чтобы добавить товар. Затем возвращаемся на сайт WordPress, и теперь мы можем видеть товар в корзине, которая выводится в нашем блоге.
Несколько заключительных слов
Готово. Теперь вы имеете успешное соединение корзины товаров на Ruby on Rails с вашим WordPress-блогом или сайтом.
Удалите print_r, стилизуйте вывод с помощью CSS, создайте несколько реальных ссылок «Добавить в корзину» (а также кнопку «Заказать», которая перенесет пользователя обратно к приложению), и вы получите завершенную интеграцию функциональной корзины товаров.
Создавайте свои API легко и быстро!
Источник: code.tutsplus.com