Подключаем SOAP веб-сервисы в Yii2

Многие сайты и некоторые серверные приложения позволяют обращаться к ним по сети посредством стандартизированного протокола SOAP. Они выкладывают открытый API, через который позволяют вызывать некоторые их методы с передачей параметров. При этом масштабы таких систем могут быть совершенно разными: получение прогноза погоды, сеть 1C крупной организации или система бронирования авиабилетов.

Серверы

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

Мы на адрес http://ws.gismeteo.ru/Weather/Weather.asmx посылаем POST-запрос на вызов метода GetSunInfo в виде XML:

POST /Weather/Weather.asmx HTTP/1.1
Host: ws.gismeteo.ru
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://ws.gismeteo.ru/GetSunInfo"
Content-Length: ...
 
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetSunInfo xmlns="http://ws.gismeteo.ru/">
      <serial>...</serial>
      <townID>...</townID>
      <date>...</date>
    </GetSunInfo>
  </soap:Body>
</soap:Envelope>

И с сервера возвращается нужный нам ответ GetSunInfoResponse в XML-формате:

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: ...
 
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <GetSunInfoResponse xmlns="http://ws.gismeteo.ru/">
            <GetSunInfoResult>
                <dt>...</dt>
                <riseMinutesOffset>...</riseMinutesOffset>
                <setMinutesOffset>...</setMinutesOffset>
                <durationMinutes>...</durationMinutes>
                <result>
                    <errorCode>OK</errorCode>
                    <errorMessage/>
                </result>
            </GetSunInfoResult>
        </GetSunInfoResponse>
    </soap:Body>
</soap:Envelope>

Аналогично представим, что некий магазин открыл SOAP-сервис базы 1С для своих филиалов по адресу http://api.site.ru/ws/webservice.1cws, закрыв его от посторонних глаз HTTP-Basic авторизацией. Теперь работники других филиалов могут подключить свои экземпляры программы к этому общему сервису и просматривать списки заказов или отгрузок из общей базы и создавать свои:

На стороне сервера нужно только запрограммировать нужные методы вроде GetOrders и опубликовать сервис.

Это даёт возможность сделать корпоративный веб-портал для работников организации, подключенный к сервису 1С. Или даже разработать мобильное приложение.

Действительно, по этому адресу мы можем послать POST-запрос GetOrders:

POST /ws/webservice.1cws HTTP/1.1
Host: api.site.ru
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://api.site.ru/#WebService:GetLastOrders"
Content-Length: ...
Authorization: Basic U2l0ZUlVU1I8YUpTM2xxM20=
 
<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://api.site.ru/">
    <SOAP-ENV:Body>
        <ns1:GetOrders>
            <ns1:Date1>2014-07-21 23:59:00</ns1:Date1>
            <ns1:Date2>2014-04-22 00:00:00</ns1:Date2>
        </ns1:GetLastOrders>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

и получить список заказов, возвращаемый в виде вложенного XML-файла в поле return:

<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header/>
    <soap:Body>
        <m:GetOrdersResponse xmlns:m="http://api.site.ru/">
            <m:return xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                &lt;?xml version="1.0"?&gt;
                &lt;Root&gt;
                    &lt;Документ&gt;
                        &lt;НомерЗаказа&gt;123&lt;/НомерЗаказа&gt;
                        &lt;ДатаЗаказа&gt;24.12.2013 15:02:17&lt;/ДатаЗаказа&gt;
                        ...
                        &lt;Статус&gt;Выполнен&lt;/Статус&gt;
                    &lt;/Документ&gt;
                    &lt;Документ&gt;
                        &lt;НомерЗаказа&gt;124&lt;/НомерЗаказа&gt;
                        &lt;ДатаЗаказа&gt;24.12.2013 16:30:42&lt;/ДатаЗаказа&gt;
                        ...
                        &lt;Статус&gt;Новый&lt;/Статус&gt;
                    &lt;/Документ&gt;
                &lt;Root&gt;
            </m:return>
        </m:GetLastOrdersResponse>
    </soap:Body>
</soap:Envelope>

Мы просто отправляем запрос на адрес http://api.site.ru/ws/webservice.1cws для вызова метода с полным именем http://api.site.ru/#WebService:GetOrders и оформляем XML запрос с использованием соответствующего пространства имён http://api.site.ru/.

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

Смотря на такие «адские» XML-структуры вам наверняка покажется, что это ужасно сложно использовать. На самом деле, это только эмоциональное первое впечатление. Как бы это ни выглядело сложным, вам не придётся разбираться в формате и что-то парсить.

В PHP для работы с веб-сервисами имеется класс SoapClient. Если вы работаете в Windows и столкнулись с отсутствием этого класса, то подключите расширение php_soap.dll в файле php.ini.

Работать с этим компонентом можно в двух режимах. Различаются они только способом формирования запросов.

Работа в WSDL-режиме

Обычно на сервере имеется специально сгенерированный WSDL-файл c описанием всех доступных методов и адресов веб-сервиса. У GisMeteo он выглядит так.

Для уже знакомого нам метода GetSunInfo в нём имеется отдельная секция с определением полей запроса и ответа:

<s:element name="GetSunInfo">
    <s:complexType>
        <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="serial" type="s:string"/>
            <s:element minOccurs="1" maxOccurs="1" name="townID" type="s:int"/>
            <s:element minOccurs="1" maxOccurs="1" name="date" type="s:dateTime"/>
        </s:sequence>
    </s:complexType>
</s:element>
<s:element name="GetSunInfoResponse">
    <s:complexType>
        <s:sequence>
        <s:element minOccurs="1" maxOccurs="1" name="GetSunInfoResult" type="tns:GetSunInfoResult"/>
        </s:sequence>
    </s:complexType>
</s:element>
<s:complexType name="GetSunInfoResult">
    <s:sequence>
        <s:element minOccurs="1" maxOccurs="1" name="dt" type="s:dateTime"/>
        <s:element minOccurs="1" maxOccurs="1" name="riseMinutesOffset" type="s:int"/>
        <s:element minOccurs="1" maxOccurs="1" name="setMinutesOffset" type="s:int"/>
        <s:element minOccurs="1" maxOccurs="1" name="durationMinutes" type="s:int"/>
        <s:element minOccurs="1" maxOccurs="1" name="result" type="tns:ServiceResult"/>
    </s:sequence>
</s:complexType>

Ниже находится информация о необходимом для его вызова значении action и используемых режимах работы:

<wsdl:operation name="GetSunInfo">
    <soap:operation soapAction="http://ws.gismeteo.ru/GetSunInfo" style="document"/>
    <wsdl:input>
        <soap:body use="literal"/>
    </wsdl:input>
    <wsdl:output>
        <soap:body use="literal"/>
    </wsdl:output>
</wsdl:operation>

И в самом конце файла расположена секция для определения location для работы по протоколам SOAP 1.1 и SOAP 1.2 соответственно:

<wsdl:service name="Weather">
    <wsdl:port name="WeatherSoap" binding="tns:WeatherSoap">
        <soap:address location="http://ws.gismeteo.ru/Weather/Weather.asmx"/>
    </wsdl:port>
    <wsdl:port name="WeatherSoap12" binding="tns:WeatherSoap12">
        <soap12:address location="http://ws.gismeteo.ru/Weather/Weather.asmx"/>
    </wsdl:port>
</wsdl:service>

Аналогичные секции присутствуют в WSDL файле, генерируемом веб-сервисом 1С.

Формат запроса заказов может быть таким:

<xs:element name="GetOrders">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="Date1" type="xs:dateTime" nillable="true"/>
            <xs:element name="Date2" type="xs:dateTime" nillable="true"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
<xs:element name="GetOrdersResponse">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="return" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

Полное имя метода с пространством имён таким:

<operation name="GetOrders">
    <soapbind:operation style="document" soapAction="http://api.site.ru/#WebService:GetOrders"/>
    <input>
        <soapbind:body use="literal"/>
    </input>
    <output>
        <soapbind:body use="literal"/>
    </output>
</operation>

А адреса доступа такими:

<service name="WebService">
    <port name="WebServiceSoap" binding="tns:WebServiceSoapBinding">
        <soapbind:address location="http://api.site.ru/ws/webservice.1cws"/>
    </port>
    <port name="WebServiceSoap12" binding="tns:WSVolhovecSoap12Binding">
        <soap12bind:address location="http://api.site.ru/ws/webservice.1cws"/>
    </port>
</service>

Теперь достаточно указать этот файл при создании объекта класса SoapClient:

$client = new SoapClient('http://ws.gismeteo.ru/Weather/Weather.asmx?WSDL');

или воспользоваться локальной копией файла:

$client = new SoapClient(__DIR__ . '/webservice.wsdl');

Так как сервис может быть закрыт HTTP Basic авторизацией, мы можем указать данные для доступа:

$client = new SoapClient(__DIR__ . '/webservice.wsdl', [
    'login' => 'soap_username',
    'password' => 'soap_password',
]);

В классе SoapClient реализован магический метод __call, который позволяет работать с удалёнными методами прямо по их имени:

$result = $client->GetSunInfo([
    'serial' => '...',
    'townID' => 57,
    'date' => '2014-01-31',
);

Результат $result не надо парсить вручную, так как он уже будет представлять из себя объект с полями, повторяющими XML-структуру ответа. Например, долготу дня можно будет получить так:

echo $result->GetSunInfoResult->durationMinutes;

Аргументы метода могут передаваться в виде массива или объекта. Попробуем, например, получить курсы валют на текущий день по протоколу SOAP с сайта Центробанка с помощью метода GetCursOnDate. В отличие от получения погоды, этот метод возвращает ответ с единственным полезным нам полем any, в котором содержится вложенный XML документ с валютами. Но ничего страшного, так как его легко будет превратить в объект с помощью SimpleXMLElement.

Создадим файл curs.php:

$wsdl = 'http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx?WSDL';
 
$client = new SoapClient($wsdl, [
    'exceptions' => 1,
    'cache_wsdl' => WSDL_CACHE_MEMORY,
]);
 
$result = $client->GetCursOnDate([
    'On_date' => date('Y-m-d'),
]);
 
$data = new SimpleXMLElement($result->GetCursOnDateResult->any);
 
foreach ($data->ValuteData->ValuteCursOnDate as $curs) {
    printf('%s = %s Руб', trim($curs->Vname), floatval($curs->Vcurs) / floatval($curs->Vnom));
    echo PHP_EOL;
}

и запустим его в консоли:

php curs.php

Через некоторое время мы увидим результат:

Австралийский доллар = 29.1544 Руб
Азербайджанский манат = 41.6884 Руб
...
Вон Республики Корея = 0.0309349 Руб
Японская иена = 0.311228 Руб

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

Вот и всё. Создаём клиент и выполняем методы. Никаких мучений с составлением и парсингом SOAP-запросов и ответов.

Работа в ручном режиме

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

В данном случае клиенту неоткуда будет брать информацию об адресе сервиса, пространствах имён и полных имён методов для SOAPAction. Поэтому адрес сервиса location и пространство имён uri надо будет указать вручную:

$location = 'http://api.site.ru/ws/webservice.1cws';
$uri = 'http://api.site.ru/'
$username = 'username';
$password = 'password';
 
$client = new SoapClient(null, [
    'use' => SOAP_LITERAL,
    'style' => SOAP_DOCUMENT,
    'location' => $location,
    'uri' => $uri,
    'login' => $username,
    'password' => $password,
    'exceptions' => 1,
    'soap_version' => SOAP_1_1,
]);

Параметром exceptions мы включаем механизм генерирования исключений, позволяющий обрабатывать их конструкцией try { ... } catch (SoapFault $e). Другими параметрами можно указать, например, используемый прокси-сервер.

Все методы класса SoapClient доступны извне. Послать команду на сервер теперь нужно непосредственным вызовом __soapCall, указав имя метода для построения XML-запроса и полное имя SOAPAction. Получим из 1С список заказов за последние 90 дней:

$date_from = (new \DateTime())->sub(new \DateInterval('P90D'))->format('Y-m-d 00:00:00');
$date_to = (new \DateTime())->format('Y-m-d 23:59:59');
 
$method = 'GetOrders';
$action = 'http://api.site.ru/#WebService:GetOrders';
$params = [
    new SoapParam($date_from, 'Date1');
    new SoapParam($date_to, 'Date2');
];
$options = [
    'soapaction' => $action,
];
 
$result = $client->__soapCall($method, $params, $options);

Метод __soapCall позаботится о построении XML-структуры запроса и вызовет метод __doRequest, который отправит запрос на сервер.

Но при желании мы можем опуститься ещё ниже и вызвать метод __doRequest. В этом случае XML-запрос нужно составить самому:

$action = '#http://api.site.ru/#WebService:GetOrders';
 
$request_xml = '<' . '?xml version="1.0" encoding="UTF-8"?' . '>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="' . $uri . '">
    <SOAP-ENV:Body>
        <ns1:GetLastOrders>
            <ns1:Date1>' . $date_from . '</ns1:Date1>
            <ns1:Date2>' . $date_to . '</ns1:Date2>
        </ns1:GetLastOrders>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
 
$response = $client->__doRequest($request_xml, $location, $action, SOAP_1_1);

Смешанное использование

Иногда встречаются ситуации, когда нужно использовать нестандартные параметры запроса. Например, специфические заголовки. Рассмотрим пример получения информации о пользователе из eBay:

$token = $_SESSION['ebay_token'];
$appId = $config['ebay']['appId'];
 
$wsdl = 'http://developer.ebay.com/webservices/latest/eBaySvc.wsdl';
 
$method = 'GetUser';
 
$client = new SOAPClient($wsdl, [
    'exceptions' => 1,
    'location' => 'https://api.sandbox.ebay.com/wsapi?callname=' . $method . '&appid=' . $appId . '&siteid=0&version=821&routing=new',
]);
 
$auth = new stdClass();
$auth->eBayAuthToken = $token;
 
$header = new SoapHeader('urn:ebay:apis:eBLBaseComponents', 'RequesterCredentials', $auth);
 
$params = [
    'Version' => 821,
    'DetailLevel' => 'ReturnSummary',
    'UserID' => '',
];
 
$response = $client->__soapCall($method, $params, null, $header);

Можно заметить, что здесь есть WSDL файл, но для работы нам нужно передавать токен авторизованного пользователя в дополнительных заголовках. И в этом случае нам помог вызов метода __soapCall.

Интеграция с веб-сервисом в Yii2

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

use SoapClient;
 
...
 
$wsdl = Yii::getAlias('@app/config/webservice.wsdl');
$client = new SoapClient($wsdl, [
    'login' => Yii::$app->params['soap_username'],
    'password' => Yii::$app->params['soap_password'],
    'exceptions' => 1,
    'soap_version' => SOAP_1_2,
    'cache_wsdl' => WSDL_CACHE_MEMORY,
]);

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

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

Вместо использования динамически создаваемых структур или ассоциативных массивов для передачи аргументов запроса:

$request = new stdClass();
$request->serial = '...';
$request->townID = 57;
$request->date = date('Y-m-d');

лучше использовать одноимённые запросам классы:

class GetSunInfo
{
    public $serial;
    public $townID;
    public $date;
}
 
$request = new GetSunInfo();
$request->serial = '...';
$request->townID = 57;
$request->date = date('Y-m-d');
 
$response = $client->GetSunInfo($request);

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

Например, создав базовый класс для наших команд, наследуемый от класса yii\base\Model:

namespace app\components\webservice\request;
 
use yii\base\Model;
 
abstract class BaseRequest extends Model {}

мы превращаем все структуры в модели и легко можем добавить в них правила валидации:

namespace app\components\webservice\request;
 
class GetSunInfo extends BaseRequest
{
    public $serial;
    public $townID;
    public $date;
 
    public function rules()
    {
        return [
            [['serial', 'townID', 'date'], 'required'],
            ['date', 'date'],
        ]
    }
}

и проверять корректность заполненных данных перед отправкой запроса:

$request = new GetSunInfo();
$request->serial = '...';
$request->townID = 57;
$request->date = date('Y-m-d');
 
if ($request->validate()) {
    $response = $client->GetSunInfo($request);
} else {
    print_r($request->getErrors());
}

И ещё один нюанс. У нас сделано так, что имя метода и класса его параметров совпадают:

$request = new GetSunInfo();
$response = $client->GetSunInfo($request);

Соответственно, имя метода можно получить из имени класса:

$method = pathinfo(str_replace('\\', '/', get_class($request)), PATHINFO_BASENAME);

и вызывать этот метод у клиента динамически любым способом из этих двух:

$response = $client->{$method}($request);
$response = call_user_func_array([$this->client, $method], [$request]);

Теперь, пользуясь этими нюансами, мы можем создать компонент приложения с единственным методом send, который будет автоматически определять имя метода и вызывать его у клиента:

namespace app\components\webservice;
 
use app\components\webservice\request\BaseRequest;
use yii\base\Component;
use SoapClient;
use Yii;
 
class WebService extends Component
{
    public $wsdl = '';
    public $username = '';
    public $password = '';
 
    /**
     * @var SoapClient
     */
    private $client;
 
    public function init()
    {
        $this->createSoapClient();
        parent::init();
    }
 
    public function send(BaseRequest $request)
    {
        $method = pathinfo(str_replace('\\', '/', get_class($request)), PATHINFO_BASENAME);
        return @call_user_func_array([$this->client, $method], [$request]);
    }
 
    protected function createSoapClient()
    {
        $wsdl = Yii::getAlias($this->wsdl);
        $this->client = new SoapClient($wsdl, [
            'trace' => 1,
            'compression' => SOAP_COMPRESSION_ACCEPT,
            'login' => $this->username,
            'password' => $this->password,
            'exceptions' => 1,
            'soap_version' => SOAP_1_2,
            'cache_wsdl' =>  WSDL_CACHE_MEMORY,
        ]);
    }
}

и подключить его в конфигурационном файле:

return [
    'components' => [
        ...
        'webservice' => [
            'class' => 'app\components\webservice\WebService',
            'wsdl' => '@app/config/webservice.wsdl',
            'username' => 'user',
            'password' => 'password',
        ],
    ],
];

чтобы теперь обращаться к нему как к Yii::$app->webservice:

$request = new GetSunInfo();
$request->serial = '...';
$request->townID = 57;
$request->date = date('Y-m-d');
 
if ($request->validate()) {
    $response = Yii::$app->webservice->send($request);
    ...
} else {
    ...
}

Этого уже вполне достаточно для нормальной работы.

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

Создадим базовый класс для ответа:

namespace app\components\webservice\response;
 
abstract class BaseResponse
{
    public $result = '';
 
    public function __construct($result)
    {
        $this->result = $result;
    }
}

Теперь отнаследуем от него класс, который будет обрабатывать ответ метода получения курса валют GetCursOnDate. Одноимённый класс, но в папке response:

namespace app\components\webservice\response;
 
class GetCursOnDate extends BaseResponse
{
    private $_data;
 
    public function getCursByCode($code)
    {
        foreach ($this->getData()->ValuteCursOnDate as $curs) {
            if ($curs->VchCode == $code) {
                return floatval($curs->Vcurs) / floatval($curs->Vnom);
            }
        }
        return null;
    }
 
    private function getData()
    {
        if ($this->_data === null) {
            $xml = $this->result->ValuteData->GetCursOnDateResult->any;
            $this->_data = new SimpleXMLElement($xml);
        }
        return $this->_data;
    }
}

Дополним наш компонент функционалом оборачивания пришедшего от клиента ответа в полученный из имени метода класс:

use app\components\webservice\request\BaseRequest;
use app\components\webservice\response\BaseResponse;
 
class WebService extends Component
{
    ...
 
    /**
     * @param BaseRequest $request
     * @return BaseResponse
     */
    public function send(BaseRequest $request)
    {
        $method = pathinfo(str_replace('\\', '/', get_class($request)), PATHINFO_BASENAME);
        $response = @call_user_func_array([$this->client, $method], [$request]);
        $class = '\app\components\webservice\response\\' . $method;
        return new $class($response);
    }
}

После этого вместо голого ответа от клиента в переменной $response мы уже получим наш объект и сможем использовать его методы.

Теперь весь код получения курса доллара через SOAP клиент занимает всего несколько строк:

use app\components\webservice\request\GetCursOnDate;
use app\components\webservice\response\GetCursOnDate as GetCursOnDateResponse;
 
$request = new GetCursOnDate();
$request->On_date = date('Y-m-d');
 
/** @var GetCursOnDateResponse $response */
$response = Yii::$app->webservice->send($request);
 
echo $response->getCursByCode('USD');

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

После того, как оставите комментарий к данной статье, советую прочесть ещё пару интересных статей:

До встречи!

Другие статьи

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

Продолжаем разработку нашего чудо-сервиса на Yii2. На прошлом уроке мы создали через Composer новый проект и дополнили его раздельной системой конфигурационных файлов. Сегодня мы внедрим в проект модульную структуру и немного лучше познакомимся с базовыми настройками и некоторыми возможностями авторефакторинга в PhpStorm IDE.

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

С момента предыдущей публикации на сайте прошло два месяца. За это время практически наступило лето, начал набирать обороты мой замороженный с декабря по апрель марафонский контентный проект, изучено много материалов в различных областях знаний. Но главное событие этой весны для некоторых программистов – это выход beta-версии нового Yii 2.0. Это не может не радовать, так как практически на наших глазах произошёл вход Yii в экосистему фреймворков нового поколения.

Комментарии

 

Дмитрий Елисеев

Интересно, но 30 писем этого выпуска рассылки подписчикам не дошли и были отклонены как спам... Подозреваю, что это ящики на mail.ru.

Ответить

 

Сергей Доровский

У меня ящик на mail.ru, письмо пришло. Спасибо за статью!

Ответить

 

Samat Zhanbekov

Здравствуйте, Дмитрий. Этот как и др. ваши статьи очень полезны, спасибо. Черканите пожалуйста подробную стать о том как сделать 1С интеграцию с yii или yii2

Ответить

 

Дмитрий Елисеев

С самим кодом на 1С я не очень помогу. Механизм как в представленном видео. А со стороны Yii2 всё как в этой статье.

Ответить

 

olegweb

Опечатка:

class GetCursOnDate

Должно быть:

class GetCursOnDate extends BaseResponse
Ответить

 

Дмитрий Елисеев

Спасибо! Исправил.

Ответить

 

Vic

Вот тут еще для красоты добавить $ в начале:

token = $_SESSION['ebay_token'];

Спасибо за интересные статьи.

Ответить

 

ldn

опечатка в кавычках:

echo $response->getCursByCode('USD");
Ответить

 

Дмитрий Елисеев

Спасибо! Исправил.

Ответить

 

Евгений

Здравствуйте, Дмитрий!
К сожалению не доступно видео из этой статьи. Или это просто скринкаст данной статьи?
Заранее благодарю!

Ответить

 

Дмитрий Елисеев

Проверил. Доступно.

Ответить

 

Дмитрий

Спасибо больше за статью! Очень помогла

У вас тут опечатка или специально зачем-то подчеркивание перед data?

private function getData()
{
    if ($this->data === null) {
        $xml = $this->result->ValuteData->GetCursOnDateResult->any;
        $this->_data = new SimpleXMLElement($xml);
    }
    return $this->_data;
}

ps: "Ваш сайт" не дает заполнить https. :)

Ответить

 

Дмитрий Елисеев

Опечатку исправил. Подчёркивание модно в Yii2.

Ответить

 

Alex Vakula

Здравствуйте!
Не подскажете как лучше подключить СМС рассылку xml запросом.
Рассылать должно как одному, так и выбраной группе.

Адрес сервера

http://sms.skysms.net/api/bulk_sm

Описание команд

sendmessage

Отправка пакета СМС сообщений.

Запрос

<?xml version="1.0" encoding="utf-8"?>
<packet version="1.0">
    <auth login="xxx" password="xxx"/>
    <command name="sendmessage">
        <message id="111" type="sms">
            <data charset="cyr">test message ::Field1::</data>
            <recipients>
                <recipient id="001" address="+380667412611" field1="test">this is a ::Field1:: message </recipient>
                <recipient id="002" address="+380973550511"/>
                <recipient id="003" address="+380505067911" field1="test22"/>
            </recipients>
        </message>
    </command>
</packet>

Ответ

<?xml version="1.0" encoding="utf-8"?>
<packet version="1.0">
    <result type="00">
        ...
    </result>
</packet>

querymessage

Запрос статуса пакета СМС сообщений.

Запрос

<?xml version="1.0" encoding="utf-8"?>
<packet version="1.0">
    <auth login="xxx" password="xxx"/>
    <command name="querymessage">
        <message smsmsgid="8FB415E38-E0A8-4106-AD12-1896F7DDC2DD"/>
    </command>
</packet>

Ответ

<?xml version="1.0" encoding="utf-8"?>
<packet version="1.0">
    <result type="00">
        ...
    </result>
</packet>
Ответить

 

Дмитрий Елисеев

Это не SOAP, а простое XML-API. Формируем XML через DomDocument и посылаем POST-запросом вручную через CURL либо через любой клиент вроде Guzzle или Yii2 HTTP Client. И ответ парсим через simplexml_load_string в объект.

Ответить

 

Alex Vakula

спасибо, попробую

Ответить

Оставить комментарий

Войти | Завести аккаунт


(никто не увидит)



Можно использовать теги <p> <ul> <li> <b> <i> <a> <pre>