├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── circle.yml ├── composer.json ├── phpspec.yml ├── spec ├── ApiAdapterSpec.php ├── ApiResolverSpec.php ├── ClientSpec.php ├── Factory │ ├── ApiAdapterFactorySpec.php │ ├── PaginatorFactorySpec.php │ └── PostFileFactorySpec.php ├── GenericApiSpec.php ├── Map │ └── ArrayUriMapSpec.php └── PaginatorSpec.php └── src ├── AdapterInterface.php ├── ApiAdapter.php ├── ApiInterface.php ├── ApiResolver.php ├── ApiResolverInterface.php ├── Client.php ├── ClientInterface.php ├── Factory ├── AdapterFactoryInterface.php ├── ApiAdapterFactory.php ├── PaginatorFactory.php ├── PaginatorFactoryInterface.php ├── PostFileFactory.php └── PostFileFactoryInterface.php ├── GenericApi.php ├── InvalidResponseFormatException.php ├── Map ├── ArrayUriMap.php └── UriMapInterface.php ├── Paginator.php └── PaginatorInterface.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /bin 3 | 4 | composer.lock -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | ### v0.1.0 5 | 6 | * Initial dev release. 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Michał Marcinkowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sylius API PHP client library 2 | ============== 3 | [![Build status...](https://img.shields.io/circleci/project/Lakion/sylius-api-php.svg)](https://circleci.com/gh/Lakion/sylius-api-php) [![Code Quality](https://img.shields.io/scrutinizer/g/Lakion/sylius-api-php.svg)](https://scrutinizer-ci.com/g/Lakion/sylius-api-php/) 4 | 5 | PHP client for RESTful Sylius API! 6 | 7 | Usage 8 | ----- 9 | 10 | Initialize client with OAuth2 authentication and mapping 11 | ```php 12 | use GuzzleHttp\Client; 13 | use Sylius\Api\Client as SyliusClient; 14 | use Sylius\Api\Map\ArrayUriMap; 15 | use Sylius\Api\ApiResolver; 16 | use CommerceGuys\Guzzle\Oauth2\GrantType\RefreshToken; 17 | use CommerceGuys\Guzzle\Oauth2\GrantType\PasswordCredentials; 18 | use CommerceGuys\Guzzle\Oauth2\Oauth2Subscriber; 19 | 20 | $oauth2Client = new Client([ 21 | 'base_url' => 'http://demo.sylius.org', 22 | ]); 23 | 24 | $config = [ 25 | 'token_url' => 'oauth/v2/token', 26 | 'client_id' => '1_demo_client', 27 | 'client_secret' => 'secret_demo_client', 28 | 'username' => 'api@example.com', 29 | 'password' => 'api' 30 | ]; 31 | 32 | $token = new PasswordCredentials($oauth2Client, $config); 33 | $refreshToken = new RefreshToken($oauth2Client, $config); 34 | 35 | $oauth2 = new Oauth2Subscriber($token, $refreshToken); 36 | 37 | // create sylius client 38 | $syliusClient = SyliusClient::createFromUrl('http://demo.sylius.org/api/', $oauth2); 39 | 40 | // initialize uri mapping for custom uris, all other uris are autogenerated (plural name of the resource, in accordance with RESTful API resource naming best practices) 41 | $uriMap = new ArrayUriMap( 42 | [ 43 | 'product-variants' => 'products/{productId}/variants', 44 | 'taxons' => 'taxonomies/{taxonomyId}/taxons', 45 | 'items' => 'orders/{orderId}/items', 46 | 'coupons' => 'promotions/{promotionId}/coupons', 47 | 'provinces' => 'countries/{countryId}/provinces' 48 | ] 49 | ); 50 | // initializer api resolver 51 | $apiResolver = new ApiResolver($syliusClient, $uriMap); 52 | 53 | ``` 54 | 55 | If you use Symfony2 framework, you can also register some services, initialize Sylius API Client and inject it to other services. 56 | 57 | ```yml 58 | 59 | parameters: 60 | app.http_client.sylius.base_url: 'http://local.sylius.dev/app_dev.php/' 61 | app.http_client.sylius.api_url: 'http://local.sylius.dev/app_dev.php/api/' 62 | app.http_client.sylius.token_url: 'oauth/v2/token' 63 | app.sylius_client.id: 1_demo_client 64 | app.sylius_client.secret: secret_demo_client 65 | app.sylius_client.username: api@example.com 66 | app.sylius_client.password: api 67 | app.sylius_taxonomy.uri: taxonomies 68 | 69 | services: 70 | #Http client that is used to obtain OAuth2 token 71 | app.http_client.sylius_oauth: 72 | class: GuzzleHttp\Client 73 | arguments: 74 | - { base_url: %app.http_client.sylius.base_url% } 75 | #Password credentials that use http client to authenticate user with given data 76 | app.oauth2.grant_type.password_credentials: 77 | class: CommerceGuys\Guzzle\Oauth2\GrantType\PasswordCredentials 78 | arguments: 79 | - @app.http_client.sylius_oauth 80 | - { token_url: %app.http_client.sylius.token_url%, client_id: %app.sylius_client.id%, client_secret: %app.sylius_client.secret%, username: %app.sylius_client.username%, password: %app.sylius_client.password% } 81 | #Refresh token that use http client to refresh oauth2 token 82 | app.oauth2.grant_type.refresh_token: 83 | class: CommerceGuys\Guzzle\Oauth2\GrantType\RefreshToken 84 | arguments: 85 | - @app.http_client.sylius_oauth 86 | - { token_url: %app.http_client.sylius.token_url%, client_id: %app.sylius_client.id%, client_secret: %app.sylius_client.secret%, username: %app.sylius_client.username%, password: %app.sylius_client.password% } 87 | #Subscriber used by Sylius API Client to provide OAuth2 authentication 88 | app.oauth2.subscriber.sylius_api: 89 | class: CommerceGuys\Guzzle\Oauth2\Oauth2Subscriber 90 | arguments: [@app.oauth2.grant_type.password_credentials, @app.oauth2.grant_type.refresh_token] 91 | #Http client that is used to connect with Sylius API 92 | app.http_client.sylius_api: 93 | class: GuzzleHttp\Client 94 | arguments: 95 | - { base_url: '%app.http_client.sylius.api_url%', defaults: { auth: 'oauth2', subscribers: [@app.oauth2.subscriber.sylius_api] } } 96 | #Sylius API Client 97 | app.api_client.sylius: 98 | class: Sylius\Api\Client 99 | arguments: [@app.http_client.sylius_api] 100 | 101 | #Example API 102 | app.sylius_taxonomy.api: 103 | class: Sylius\Api\GenericApi 104 | arguments: [@app.api_client.sylius, %app.sylius_taxonomy.uri%] 105 | 106 | #Uri map with custom uris 107 | app.api_resolver.uri_map.sylius: 108 | class: Sylius\Api\Map\ArrayUriMap 109 | arguments: 110 | - { product-variants: 'products/{productId}/variants', taxons: 'taxonomies/{taxonomyId}/taxons', items: 'orders/{orderId}/items', coupons: 'promotions/{promotionId}/coupons', provinces: 'countries/{countryId}/provinces' } 111 | #Initialize Api resolver with uri map 112 | app.api_resolver.sylius: 113 | class: Sylius\Api\ApiResolver 114 | arguments: [@app.api_client.sylius, @app.api_resolver.uri_map.sylius] 115 | 116 | ``` 117 | 118 | **Get API for resource** 119 | ```php 120 | #Obtain api using api resolver 121 | $taxonomiesApi = $apiResolver->getApi('taxonomies'); 122 | #or register it as a service and obtain it from container 123 | $taxonomiesApi = $container->get('app.sylius_taxonomy.api'); 124 | $taxonsApi = $apiResolver->getApi('taxons'); 125 | ``` 126 | 127 | **Create resource** 128 | ```php 129 | // right now because of translations you have to do it this way (will be changed in the nearest future to simply allow for ['name' => 'My taxonomy']) 130 | $taxonomy = $taxonomiesApi->create(['translations' => ['en' => ['name' => 'My taxonomy']]]); 131 | // for custom uris you have to specify uri parameters ('taxonomyId') 132 | $taxon = $taxonsApi->create(['translations' => ['en' => ['name' => 'My taxon']]], ['taxonomyId' => $taxonomy['id']); 133 | ``` 134 | 135 | **Update resource** 136 | ```php 137 | $taxonomy = $taxonomiesApi->update($taxonomyId, ['translations' => ['en' => ['name' => 'My taxonomy updated']]]); 138 | // for custom uris you have to specify uri parameters ('taxonomyId') 139 | $taxon = $taxonsApi->update($taxonId, $taxonomy['translations' => ['en' => ['name' => 'My taxon' updated]]], ['taxonomyId' => $taxonomyId]); 140 | ``` 141 | 142 | **Get resource** 143 | ```php 144 | $taxonomy = $taxonomiesApi->get($taxonomyId); 145 | // for custom uris you have to specify uri parameters ('taxonomyId') 146 | $taxon = $taxonsApi->get($taxonId, ['taxonomyId' => $taxonomyId]); 147 | ``` 148 | 149 | **Get all resources** 150 | 151 | * Get paginated resources 152 | ```php 153 | // you can getPaginated resources (it returns array) 154 | $taxonomies = $taxonomiesApi->getPaginated($page, $limitPerPage); 155 | $taxons = $taxonsApi->getPaginated($page, $limitPerPage, ['taxonomyId' => $taxonomyId]); 156 | ``` 157 | * Create paginator for resource 158 | ```php 159 | // you can create taxonomies paginator 160 | $taxonomiesPaginator = $taxonomiesApi->createPaginator($limitPerPage); 161 | // for custom uris you have to specify uri parameters ('taxonomyId'), rest is the same 162 | $taxonsPaginator = $taxonsApi->createPaginator($limitPerPage, ['taxonomyId' => $taxonomyId]); 163 | // returns array of elements for current page 164 | $taxonomies = $taxonomiesPaginator->getCurrentPageResults(); 165 | // ... (do something with current page taxonomies) 166 | // and go through all results 167 | while ($taxonomiesPaginator->hasNextPage()) { 168 | $taxonomiesPaginator->nextPage(); 169 | $taxonomies = $taxonomiesPaginator->getCurrentPageResults(); 170 | // ... (do something with current page taxonomies) 171 | } 172 | ``` 173 | * Get all resources 174 | ```php 175 | // getAll method gets all resources, so you have to be careful about the memory usage 176 | $taxonomies = $taxonomiesApi->getAll(); 177 | // for custom uris you have to specify uri parameters ('taxonomyId') 178 | $taxons = $taxonsApi->getAll(['taxonomyId' => $taxonomyId]); 179 | ``` 180 | 181 | **Delete resource** 182 | ```php 183 | // returns whether the deletion was successful or not 184 | // for custom uris you have to specify uri parameters ('taxonomyId') 185 | $trueOrFalse = $taxonsApi->delete($taxonId, ['taxonomyId' => $taxonomyId]); 186 | $trueOrFalse = $taxonomiesApi->delete($taxonomyId); 187 | ``` 188 | 189 | Bug tracking 190 | ------------ 191 | 192 | This component uses [GitHub issues](https://github.com/Lakion/sylius-api-php/issues). 193 | If you have found bug, please create an issue. 194 | 195 | MIT License 196 | ----------- 197 | 198 | License can be found [here](https://github.com/Lakion/sylius-api-php/tree/master/LICENSE.md). 199 | 200 | Authors 201 | ------- 202 | 203 | The library was originally created by: 204 | 205 | * Michał Marcinkowski 206 | 207 | See the list of [contributors](https://github.com/Lakion/sylius-api-php/graphs/contributors). 208 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | php: 3 | version: 5.4.4 4 | test: 5 | override: 6 | - bin/phpspec run -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lakion/sylius-api-php", 3 | "description": "Sylius API PHP client library", 4 | "license": "MIT", 5 | "require": { 6 | "php": ">=5.4.0", 7 | "guzzlehttp/guzzle": "~5.0", 8 | "commerceguys/guzzle-oauth2-plugin": "~2.0" 9 | }, 10 | "require-dev": { 11 | "phpspec/phpspec": "~2.0" 12 | }, 13 | "config": { 14 | "bin-dir": "bin" 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Michal Marcinkowski", 19 | "email": "michal.marcinkowski@lakion.com" 20 | } 21 | ], 22 | "autoload": { 23 | "psr-4": { "Sylius\\Api\\": "src/" } 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.0-dev" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /phpspec.yml: -------------------------------------------------------------------------------- 1 | suites: 2 | main: 3 | namespace: Sylius\Api 4 | psr4_prefix: Sylius\Api 5 | src_path: src 6 | -------------------------------------------------------------------------------- /spec/ApiAdapterSpec.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class ApiAdapterSpec extends ObjectBehavior 21 | { 22 | function let(ApiInterface $api) 23 | { 24 | $this->beConstructedWith($api); 25 | } 26 | 27 | function it_is_initializable() 28 | { 29 | $this->shouldHaveType('Sylius\Api\ApiAdapter'); 30 | } 31 | 32 | function it_implements_adapter_interface() 33 | { 34 | $this->shouldImplement('Sylius\Api\AdapterInterface'); 35 | } 36 | 37 | function it_gets_number_of_results($api) 38 | { 39 | $api->getPaginated(['page' => 1, 'limit' => 2], [])->willReturn( 40 | array( 41 | 'page' => 1, 42 | 'limit' => 2, 43 | 'pages' => 2, 44 | 'total' => 3, 45 | '_embedded' => 46 | array( 47 | 'items' => 48 | array ( 49 | 0 => 50 | array( 51 | 'id' => 1, 52 | 'email' => 'chelsie.witting@example.com', 53 | 'username' => 'chelsie.witting@example.com', 54 | ), 55 | 1 => 56 | array( 57 | 'id' => 2, 58 | 'email' => 'chelsie.witting1@example.com', 59 | 'username' => 'chelsie.witting1@example.com', 60 | ), 61 | ), 62 | ), 63 | ) 64 | ); 65 | 66 | $this->getNumberOfResults(['page' => 1, 'limit' => 2])->shouldReturn(3); 67 | } 68 | 69 | function it_caches_results_on_get_number_of_results($api) 70 | { 71 | $api->getPaginated(['page' => 1, 'limit' => 2], [])->willReturn( 72 | array( 73 | 'page' => 1, 74 | 'limit' => 2, 75 | 'pages' => 2, 76 | 'total' => 3, 77 | '_embedded' => 78 | array( 79 | 'items' => 80 | array ( 81 | 0 => 82 | array( 83 | 'id' => 1, 84 | 'email' => 'chelsie.witting@example.com', 85 | 'username' => 'chelsie.witting@example.com', 86 | ), 87 | 1 => 88 | array( 89 | 'id' => 2, 90 | 'email' => 'chelsie.witting1@example.com', 91 | 'username' => 'chelsie.witting1@example.com', 92 | ), 93 | ), 94 | ), 95 | ) 96 | )->shouldBeCalledTimes(1); 97 | 98 | $this->getNumberOfResults(['page' => 1, 'limit' => 2]); 99 | $this->getNumberOfResults(['page' => 1, 'limit' => 2]); 100 | 101 | $this->getResults(['page' => 1, 'limit' => 2])->shouldReturn(array( 102 | array( 103 | 'id' => 1, 104 | 'email' => 'chelsie.witting@example.com', 105 | 'username' => 'chelsie.witting@example.com', 106 | ), 107 | array( 108 | 'id' => 2, 109 | 'email' => 'chelsie.witting1@example.com', 110 | 'username' => 'chelsie.witting1@example.com', 111 | ), 112 | )); 113 | } 114 | 115 | function it_gets_fresh_results_on_different_parameters_on_get_number_of_results($api) 116 | { 117 | $api->getPaginated(['page' => 1, 'limit' => 2], [])->willReturn( 118 | array( 119 | 'page' => 1, 120 | 'limit' => 2, 121 | 'pages' => 2, 122 | 'total' => 3, 123 | '_embedded' => 124 | array( 125 | 'items' => 126 | array ( 127 | 0 => 128 | array( 129 | 'id' => 1, 130 | 'email' => 'chelsie.witting@example.com', 131 | 'username' => 'chelsie.witting@example.com', 132 | ), 133 | 1 => 134 | array( 135 | 'id' => 2, 136 | 'email' => 'chelsie.witting1@example.com', 137 | 'username' => 'chelsie.witting1@example.com', 138 | ), 139 | ), 140 | ), 141 | ) 142 | ); 143 | $api->getPaginated(['page' => 2, 'limit' => 2], [])->willReturn( 144 | array( 145 | 'page' => 2, 146 | 'limit' => 2, 147 | 'pages' => 2, 148 | 'total' => 3, 149 | '_embedded' => 150 | array( 151 | 'items' => 152 | array ( 153 | 0 => 154 | array( 155 | 'id' => 3, 156 | 'email' => 'chelsie.witting2@example.com', 157 | 'username' => 'chelsie.witting2@example.com', 158 | ), 159 | ), 160 | ), 161 | ) 162 | )->shouldBeCalledTimes(1); 163 | 164 | $this->getNumberOfResults(['page' => 1, 'limit' => 2]); 165 | $this->getNumberOfResults(['page' => 2, 'limit' => 2]); 166 | 167 | $this->getResults(['page' => 2, 'limit' => 2])->shouldReturn(array( 168 | array( 169 | 'id' => 3, 170 | 'email' => 'chelsie.witting2@example.com', 171 | 'username' => 'chelsie.witting2@example.com', 172 | ), 173 | )); 174 | } 175 | 176 | function it_returns_fresh_results_on_get_number_of_results($api) 177 | { 178 | $api->getPaginated(['page' => 1, 'limit' => 2], [])->willReturn( 179 | array( 180 | 'page' => 1, 181 | 'limit' => 2, 182 | 'pages' => 2, 183 | 'total' => 3, 184 | '_embedded' => 185 | array( 186 | 'items' => 187 | array ( 188 | 0 => 189 | array( 190 | 'id' => 1, 191 | 'email' => 'chelsie.witting@example.com', 192 | 'username' => 'chelsie.witting@example.com', 193 | ), 194 | 1 => 195 | array( 196 | 'id' => 2, 197 | 'email' => 'chelsie.witting1@example.com', 198 | 'username' => 'chelsie.witting1@example.com', 199 | ), 200 | ), 201 | ), 202 | ) 203 | ); 204 | $api->getPaginated(['page' => 2, 'limit' => 2], [])->willReturn( 205 | array( 206 | 'page' => 2, 207 | 'limit' => 2, 208 | 'pages' => 2, 209 | 'total' => 3, 210 | '_embedded' => 211 | array( 212 | 'items' => 213 | array ( 214 | 0 => 215 | array( 216 | 'id' => 3, 217 | 'email' => 'chelsie.witting2@example.com', 218 | 'username' => 'chelsie.witting2@example.com', 219 | ), 220 | ), 221 | ), 222 | ) 223 | ); 224 | 225 | $this->getNumberOfResults(['page' => 1, 'limit' => 2]); 226 | 227 | $this->getResults(['page' => 2, 'limit' => 2])->shouldReturn(array( 228 | array( 229 | 'id' => 3, 230 | 'email' => 'chelsie.witting2@example.com', 231 | 'username' => 'chelsie.witting2@example.com', 232 | ), 233 | )); 234 | } 235 | 236 | function it_gets_number_of_results_for_a_specific_uri_parameters($api) 237 | { 238 | $api->getPaginated(['page' => 1, 'limit' => 2], ['parentId' => 1])->willReturn( 239 | array( 240 | 'page' => 1, 241 | 'limit' => 2, 242 | 'pages' => 2, 243 | 'total' => 3, 244 | '_embedded' => 245 | array( 246 | 'items' => 247 | array ( 248 | 0 => 249 | array( 250 | 'id' => 1, 251 | 'email' => 'chelsie.witting@example.com', 252 | 'username' => 'chelsie.witting@example.com', 253 | ), 254 | 1 => 255 | array( 256 | 'id' => 2, 257 | 'email' => 'chelsie.witting1@example.com', 258 | 'username' => 'chelsie.witting1@example.com', 259 | ), 260 | ), 261 | ), 262 | ) 263 | ); 264 | 265 | $this->getNumberOfResults(['page' => 1, 'limit' => 2], ['parentId' => 1])->shouldReturn(3); 266 | } 267 | 268 | function it_gets_results($api) 269 | { 270 | $api->getPaginated(['page' => 1, 'limit' => 10], [])->willReturn( 271 | array( 272 | 'page' => 1, 273 | 'limit' => 10, 274 | 'pages' => 1, 275 | 'total' => 3, 276 | '_embedded' => 277 | array( 278 | 'items' => 279 | array ( 280 | 0 => 281 | array( 282 | 'id' => 1, 283 | 'email' => 'chelsie.witting@example.com', 284 | 'username' => 'chelsie.witting@example.com', 285 | ), 286 | 1 => 287 | array( 288 | 'id' => 2, 289 | 'email' => 'chelsie.witting1@example.com', 290 | 'username' => 'chelsie.witting1@example.com', 291 | ), 292 | 2 => 293 | array( 294 | 'id' => 3, 295 | 'email' => 'chelsie.witting2@example.com', 296 | 'username' => 'chelsie.witting2@example.com', 297 | ), 298 | ), 299 | ), 300 | ) 301 | ); 302 | 303 | $this->getResults(['page' => 1, 'limit' => 10])->shouldReturn( 304 | array ( 305 | array( 306 | 'id' => 1, 307 | 'email' => 'chelsie.witting@example.com', 308 | 'username' => 'chelsie.witting@example.com', 309 | ), 310 | array( 311 | 'id' => 2, 312 | 'email' => 'chelsie.witting1@example.com', 313 | 'username' => 'chelsie.witting1@example.com', 314 | ), 315 | array( 316 | 'id' => 3, 317 | 'email' => 'chelsie.witting2@example.com', 318 | 'username' => 'chelsie.witting2@example.com', 319 | ), 320 | ) 321 | ); 322 | } 323 | 324 | function it_gets_results_for_a_specific_uri_parameters($api) 325 | { 326 | $api->getPaginated(['page' => 1, 'limit' => 10], ['parentId' => 1])->willReturn( 327 | array( 328 | 'page' => 1, 329 | 'limit' => 10, 330 | 'pages' => 1, 331 | 'total' => 3, 332 | '_embedded' => 333 | array( 334 | 'items' => 335 | array ( 336 | 0 => 337 | array( 338 | 'id' => 1, 339 | 'email' => 'chelsie.witting@example.com', 340 | 'username' => 'chelsie.witting@example.com', 341 | ), 342 | 1 => 343 | array( 344 | 'id' => 2, 345 | 'email' => 'chelsie.witting1@example.com', 346 | 'username' => 'chelsie.witting1@example.com', 347 | ), 348 | 2 => 349 | array( 350 | 'id' => 3, 351 | 'email' => 'chelsie.witting2@example.com', 352 | 'username' => 'chelsie.witting2@example.com', 353 | ), 354 | ), 355 | ), 356 | ) 357 | ); 358 | $this->getResults(['page' => 1, 'limit' => 10], ['parentId' => 1])->shouldReturn( 359 | array ( 360 | array( 361 | 'id' => 1, 362 | 'email' => 'chelsie.witting@example.com', 363 | 'username' => 'chelsie.witting@example.com', 364 | ), 365 | array( 366 | 'id' => 2, 367 | 'email' => 'chelsie.witting1@example.com', 368 | 'username' => 'chelsie.witting1@example.com', 369 | ), 370 | array( 371 | 'id' => 3, 372 | 'email' => 'chelsie.witting2@example.com', 373 | 'username' => 'chelsie.witting2@example.com', 374 | ), 375 | ) 376 | ); 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /spec/ApiResolverSpec.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class ApiResolverSpec extends ObjectBehavior 23 | { 24 | function let(ClientInterface $client, UriMapInterface $uriMap) 25 | { 26 | $uriMap->getUri('products')->willReturn('products'); 27 | $this->beConstructedWith($client, $uriMap); 28 | } 29 | 30 | function it_is_initializable() 31 | { 32 | $this->shouldHaveType('Sylius\Api\ApiResolver'); 33 | } 34 | 35 | function it_should_implement_api_resolver_interface() 36 | { 37 | $this->shouldImplement('Sylius\Api\ApiResolverInterface'); 38 | } 39 | 40 | function its_resolve_function_should_return_api_interface_instance() 41 | { 42 | $this->getApi('products')->shouldReturnAnInstanceOf('Sylius\Api\ApiInterface'); 43 | } 44 | 45 | function it_should_use_uri_map_to_resolve_api($uriMap) 46 | { 47 | $uriMap->getUri('products')->shouldBeCalled(); 48 | $this->getApi('products')->shouldReturnAnInstanceOf('Sylius\Api\ApiInterface'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spec/ClientSpec.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class ClientSpec extends ObjectBehavior 25 | { 26 | function let(HttpClientInterface $httpClient, PostFileFactoryInterface $postFileFactory) 27 | { 28 | $httpClient->getBaseUrl()->willReturn('http://demo.sylius.org/api/'); 29 | $this->beConstructedWith($httpClient, $postFileFactory); 30 | } 31 | 32 | function it_is_initializable() 33 | { 34 | $this->shouldHaveType('Sylius\Api\Client'); 35 | } 36 | 37 | function it_implements_client_interface() 38 | { 39 | $this->shouldImplement('Sylius\Api\ClientInterface'); 40 | } 41 | 42 | function it_sends_get_request_to_the_given_url($httpClient) 43 | { 44 | $httpClient->get('/uri', ['query' => []])->shouldBeCalled(); 45 | $this->get('/uri'); 46 | } 47 | 48 | function it_sends_post_request_to_the_given_url_with_a_given_body($httpClient, RequestInterface $request) 49 | { 50 | $httpClient->createRequest('POST', '/uri', ['body' => ['key' => 'value']])->willReturn($request); 51 | $httpClient->createRequest('POST', '/uri', ['body' => ['key' => 'value']])->shouldBeCalled(); 52 | $httpClient->send($request)->shouldBeCalled(); 53 | $this->post('/uri', ['key' => 'value']); 54 | } 55 | 56 | function it_sends_post_request_to_the_given_url_with_a_given_body_with_given_files($httpClient, RequestInterface $request, PostBodyInterface $postbody, $postFileFactory, PostFileInterface $file1, PostFileInterface $file2) 57 | { 58 | $httpClient->createRequest('POST', '/uri', ['body' => ['key' => 'value']])->willReturn($request); 59 | $request->getBody()->willReturn($postbody); 60 | $postFileFactory->create('images[0][file]', 'path/to/file1.jpg')->willReturn($file1); 61 | $postFileFactory->create('images[1][file]', 'path/to/file2.jpg')->willReturn($file2); 62 | 63 | $httpClient->createRequest('POST', '/uri', ['body' => ['key' => 'value']])->shouldBeCalled(); 64 | $postbody->addFile($file1)->shouldBeCalled(); 65 | $postbody->addFile($file2)->shouldBeCalled(); 66 | $httpClient->send($request)->shouldBeCalled(); 67 | 68 | $this->post('/uri', ['key' => 'value'], ['images[0][file]' => 'path/to/file1.jpg', 'images[1][file]' => 'path/to/file2.jpg']); 69 | } 70 | 71 | function it_sends_patch_request_to_the_given_url_with_a_given_body($httpClient) 72 | { 73 | $httpClient->patch('/uri', ['body' => ['key' => 'value']])->shouldBeCalled(); 74 | $this->patch('/uri', ['key' => 'value']); 75 | } 76 | 77 | function it_sends_put_request_to_the_given_url_with_a_given_body($httpClient) 78 | { 79 | $httpClient->put('/uri', ['body' => ['key' => 'value']])->shouldBeCalled(); 80 | $this->put('/uri', ['key' => 'value']); 81 | } 82 | 83 | function it_sends_delete_request_to_the_given_url($httpClient) 84 | { 85 | $httpClient->delete('/uri')->shouldBeCalled(); 86 | $this->delete('/uri'); 87 | } 88 | 89 | function it_gets_scheme_and_host() 90 | { 91 | $this->getSchemeAndHost()->shouldReturn('http://demo.sylius.org'); 92 | } 93 | 94 | function it_creates_client_from_url() 95 | { 96 | $this::createFromUrl('http://demo.sylius.org/api/')->shouldReturnAnInstanceOf('Sylius\Api\ClientInterface'); 97 | } 98 | 99 | function it_creates_client_from_url_with_scheme_and_host() 100 | { 101 | $this::createFromUrl('http://demo.sylius.org/api/')->getSchemeAndHost()->shouldReturn('http://demo.sylius.org'); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /spec/Factory/ApiAdapterFactorySpec.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class ApiAdapterFactorySpec extends ObjectBehavior 21 | { 22 | function let(ApiInterface $api) 23 | { 24 | $this->beConstructedWith($api); 25 | } 26 | 27 | function it_is_initializable() 28 | { 29 | $this->shouldHaveType('Sylius\Api\Factory\ApiAdapterFactory'); 30 | } 31 | 32 | function it_implements_adapter_interface() 33 | { 34 | $this->shouldImplement('Sylius\Api\Factory\AdapterFactoryInterface'); 35 | } 36 | 37 | function it_returns_api_adapter_interface() 38 | { 39 | $this->create()->shouldImplement('Sylius\Api\AdapterInterface'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spec/Factory/PaginatorFactorySpec.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class PaginatorFactorySpec extends ObjectBehavior 21 | { 22 | function it_is_initializable() 23 | { 24 | $this->shouldHaveType('Sylius\Api\Factory\PaginatorFactory'); 25 | } 26 | 27 | function it_implements_paginator_factory_interface() 28 | { 29 | $this->shouldImplement('Sylius\Api\Factory\PaginatorFactoryInterface'); 30 | } 31 | 32 | function it_returns_api_adapter_interface(AdapterInterface $adapter) 33 | { 34 | $this->create($adapter, ['limit' => 10])->shouldImplement('Sylius\Api\PaginatorInterface'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spec/Factory/PostFileFactorySpec.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class PostFileFactorySpec extends ObjectBehavior 20 | { 21 | function it_is_initializable() 22 | { 23 | $this->shouldHaveType('Sylius\Api\Factory\PostFileFactory'); 24 | } 25 | 26 | function it_implements_paginator_factory_interface() 27 | { 28 | $this->shouldImplement('Sylius\Api\Factory\PostFileFactoryInterface'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spec/GenericApiSpec.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class GenericApiSpec extends ObjectBehavior 27 | { 28 | function let(ClientInterface $client, AdapterFactoryInterface $adapterFactory, PaginatorFactoryInterface $paginatorFactory, AdapterInterface $adapter) 29 | { 30 | $adapterFactory->create()->willReturn($adapter); 31 | $this->beConstructedWith($client, 'uri', $adapterFactory, $paginatorFactory); 32 | } 33 | 34 | function it_validates_that_uri_is_given($client) 35 | { 36 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$client, null]); 37 | } 38 | 39 | function it_validates_that_uri_is_not_empty($client) 40 | { 41 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$client, '']); 42 | } 43 | 44 | function it_throws_exception_if_uri_is_not_string($client) 45 | { 46 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$client, 1]); 47 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$client, 1.1]); 48 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$client, true]); 49 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$client, array()]); 50 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$client, new \stdClass()]); 51 | } 52 | 53 | function it_is_initializable() 54 | { 55 | $this->shouldHaveType('Sylius\Api\GenericApi'); 56 | } 57 | 58 | function it_implements_api_interface() 59 | { 60 | $this->shouldImplement('Sylius\Api\ApiInterface'); 61 | } 62 | 63 | function it_gets_resource_by_id($client, ResponseInterface $response) 64 | { 65 | $response->getHeader('Content-Type')->willReturn('application/json'); 66 | $response->json()->willReturn(['id' => 1, 'name' => 'Resource name']); 67 | $client->get('uri/1')->willReturn($response)->shouldBeCalled(); 68 | 69 | $this->get(1)->shouldReturn(['id' => 1, 'name' => 'Resource name']); 70 | } 71 | 72 | function it_gets_resource_by_id_for_a_specific_uri_with_uri_parameters($client, $adapterFactory, $paginatorFactory, ResponseInterface $response) 73 | { 74 | $this->beConstructedWith($client, 'parentUri/{parentId}/uri', $adapterFactory, $paginatorFactory); 75 | $response->getHeader('Content-Type')->willReturn('application/json'); 76 | $response->json()->willReturn(['id' => 1, 'name' => 'Resource name']); 77 | $client->get('parentUri/2/uri/1')->willReturn($response)->shouldBeCalled(); 78 | 79 | $this->get(1, ['parentId' => 2] )->shouldReturn(['id' => 1, 'name' => 'Resource name']); 80 | } 81 | 82 | function it_gets_resource_by_id_for_a_specific_uri_with_multiple_uri_parameters($client, $adapterFactory, $paginatorFactory, ResponseInterface $response) 83 | { 84 | $this->beConstructedWith($client, 'parentUri/{parentId}/secondParentUri/{secondParentId}/uri', $adapterFactory, $paginatorFactory); 85 | $response->getHeader('Content-Type')->willReturn('application/json'); 86 | $response->json()->willReturn(['id' => 1, 'name' => 'Resource name']); 87 | $client->get('parentUri/2/secondParentUri/1/uri/1')->willReturn($response)->shouldBeCalled(); 88 | 89 | $this->get(1, ['parentId' => 2, 'secondParentId' => 1] )->shouldReturn(['id' => 1, 'name' => 'Resource name']); 90 | } 91 | 92 | function it_gets_paginated_resources($client, ResponseInterface $response) 93 | { 94 | $response->getHeader('Content-Type')->willReturn('application/json'); 95 | $response->json()->willReturn(['a', 'b', 'c']); 96 | $client->get('uri/', ['page' => 1, 'limit' => 10])->willReturn($response)->shouldBeCalled(); 97 | 98 | $this->getPaginated()->shouldReturn(['a', 'b', 'c']); 99 | } 100 | 101 | function it_gets_paginated_resources_by_page($client, ResponseInterface $response) 102 | { 103 | $response->getHeader('Content-Type')->willReturn('application/json'); 104 | $response->json()->willReturn(['a', 'b', 'c']); 105 | $client->get('uri/', ['page' => 3, 'limit' => 10])->willReturn($response)->shouldBeCalled(); 106 | 107 | $this->getPaginated(['page' => 3])->shouldReturn(['a', 'b', 'c']); 108 | } 109 | 110 | function it_gets_paginated_resources_by_page_with_limit($client, ResponseInterface $response) 111 | { 112 | $response->getHeader('Content-Type')->willReturn('application/json'); 113 | $response->json()->willReturn(['a', 'b', 'c']); 114 | $client->get('uri/', ['page' => 2, 'limit' => 15])->willReturn($response)->shouldBeCalled(); 115 | 116 | $this->getPaginated(['page' => 2, 'limit' => 15])->shouldReturn(['a', 'b', 'c']); 117 | } 118 | 119 | function it_gets_paginated_resources_by_page_with_limit_for_a_specific_uri_with_uri_parameters($client, $adapterFactory, $paginatorFactory, ResponseInterface $response) 120 | { 121 | $this->beConstructedWith($client, 'parentUri/{parentId}/uri', $adapterFactory, $paginatorFactory); 122 | $response->getHeader('Content-Type')->willReturn('application/json'); 123 | $response->json()->willReturn(['a', 'b', 'c']); 124 | $client->get('parentUri/1/uri/', ['page' => 2, 'limit' => 15])->willReturn($response)->shouldBeCalled(); 125 | 126 | $this->getPaginated(['page' => 2, 'limit' => 15], ['parentId' => 1])->shouldReturn(['a', 'b', 'c']); 127 | } 128 | 129 | function it_creates_resource_with_body($client, ResponseInterface $response) 130 | { 131 | $response->getHeader('Content-Type')->willReturn('application/json'); 132 | $response->json()->willReturn(['id' => 1, 'field1' => 'field1Value', 'field2' => 'field2Value']); 133 | $client->post('uri/', ['field1' => 'field1Value', 'field2' => 'field2Value'], []) 134 | ->willReturn($response)->shouldBeCalled(); 135 | 136 | $this->create(['field1' => 'field1Value', 'field2' => 'field2Value'])->shouldReturn(['id' => 1, 'field1' => 'field1Value', 'field2' => 'field2Value']); 137 | } 138 | 139 | function it_creates_resource_with_body_and_files($client, ResponseInterface $response) 140 | { 141 | $response->getHeader('Content-Type')->willReturn('application/json'); 142 | $response->json()->willReturn(['id' => 1, 'field1' => 'field1Value', 'field2' => 'field2Value', 'images[0][file]' => 'path/to/file1.jpg']); 143 | $client->post('uri/', ['field1' => 'field1Value', 'field2' => 'field2Value'], ['images[0][file]' => 'path/to/file1.jpg']) 144 | ->willReturn($response)->shouldBeCalled(); 145 | 146 | $this->create(['field1' => 'field1Value', 'field2' => 'field2Value'], [], ['images[0][file]' => 'path/to/file1.jpg']) 147 | ->shouldReturn(['id' => 1, 'field1' => 'field1Value', 'field2' => 'field2Value', 'images[0][file]' => 'path/to/file1.jpg']); 148 | } 149 | 150 | function it_creates_resource_with_body_for_a_specific_uri_with_uri_parameters($client, $adapterFactory, $paginatorFactory, ResponseInterface $response) 151 | { 152 | $this->beConstructedWith($client, 'parentUri/{parentId}/uri', $adapterFactory, $paginatorFactory); 153 | $response->getHeader('Content-Type')->willReturn('application/json'); 154 | $response->json()->willReturn(['id' => 1, 'field1' => 'field1Value', 'field2' => 'field2Value']); 155 | $client->post('parentUri/2/uri/', ['field1' => 'field1Value', 'field2' => 'field2Value'], []) 156 | ->willReturn($response)->shouldBeCalled(); 157 | 158 | $this->create(['field1' => 'field1Value', 'field2' => 'field2Value'], ['parentId' => 2]) 159 | ->shouldReturn(['id' => 1, 'field1' => 'field1Value', 'field2' => 'field2Value']); 160 | } 161 | 162 | function it_updates_resource_with_body($client, ResponseInterface $response) 163 | { 164 | $response->getStatusCode()->willReturn(204); 165 | $client->patch('uri/1', ['field1' => 'field1Value', 'field2' => 'field2Value']) 166 | ->willReturn($response)->shouldBeCalled(); 167 | 168 | $this->update(1, ['field1' => 'field1Value', 'field2' => 'field2Value'])->shouldReturn(true); 169 | } 170 | 171 | function it_updates_resource_with_body_and_files($client, ResponseInterface $response) 172 | { 173 | $response->getStatusCode()->willReturn(204); 174 | $client->post('uri/1', ['field1' => 'field1Value', 'field2' => 'field2Value'], ['images[0][file]' => 'path/to/file1.jpg']) 175 | ->willReturn($response)->shouldBeCalled(); 176 | 177 | $this->update(1, ['field1' => 'field1Value', 'field2' => 'field2Value'], [], ['images[0][file]' => 'path/to/file1.jpg'])->shouldReturn(true); 178 | } 179 | 180 | function it_updates_resource_with_body_for_a_specific_uri_with_uri_parameters($client, $adapterFactory, $paginatorFactory, ResponseInterface $response) 181 | { 182 | $this->beConstructedWith($client, 'parentUri/{parentId}/uri', $adapterFactory, $paginatorFactory); 183 | $response->getStatusCode()->willReturn(204); 184 | $client->patch('parentUri/1/uri/2', ['field1' => 'field1Value', 'field2' => 'field2Value']) 185 | ->willReturn($response)->shouldBeCalled(); 186 | 187 | $this->update(2, ['field1' => 'field1Value', 'field2' => 'field2Value'], ['parentId' => 1])->shouldReturn(true); 188 | } 189 | 190 | function it_returns_false_if_resource_update_was_not_successful($client, ResponseInterface $response) 191 | { 192 | $response->getStatusCode()->willReturn(400); 193 | $client->patch('uri/1', ['field1' => 'field1Value', 'field2' => 'field2Value'])->willReturn($response)->shouldBeCalled(); 194 | 195 | $this->update(1, ['field1' => 'field1Value', 'field2' => 'field2Value'])->shouldReturn(false); 196 | } 197 | 198 | function it_deletes_resource_by_id($client, ResponseInterface $response) 199 | { 200 | $response->getStatusCode()->willReturn(204); 201 | $client->delete('uri/1')->willReturn($response)->shouldBeCalled(); 202 | 203 | $this->delete(1)->shouldReturn(true); 204 | } 205 | 206 | function it_deletes_resource_by_id_for_a_specific_uri_with_uri_parameters($client, $adapterFactory, $paginatorFactory, ResponseInterface $response) 207 | { 208 | $this->beConstructedWith($client, 'parentUri/{parentId}/uri', $adapterFactory, $paginatorFactory); 209 | $response->getStatusCode()->willReturn(204); 210 | $client->delete('parentUri/1/uri/2')->willReturn($response)->shouldBeCalled(); 211 | 212 | $this->delete(2, ['parentId' => 1])->shouldReturn(true); 213 | } 214 | 215 | function it_returns_false_if_resource_deletion_was_not_successful($client, ResponseInterface $response) 216 | { 217 | $response->getStatusCode()->willReturn(400); 218 | $client->delete('uri/1')->willReturn($response)->shouldBeCalled(); 219 | 220 | $this->delete(1)->shouldReturn(false); 221 | } 222 | 223 | function it_gets_all_resources($adapter, $paginatorFactory, PaginatorInterface $paginator) 224 | { 225 | $paginatorFactory->create($adapter, ['limit' => 100], [])->willReturn($paginator)->shouldBeCalled(); 226 | $paginator->getCurrentPageResults()->willReturn(['a', 'b', 'c']); 227 | $paginator->hasNextPage()->willReturn(false); 228 | $paginator->nextPage()->shouldNotBeCalled(); 229 | 230 | $this->getAll()->shouldReturn(['a', 'b', 'c']); 231 | } 232 | 233 | function it_gets_all_resources_with_few_pages($adapter, $paginatorFactory, PaginatorInterface $paginator) 234 | { 235 | $paginatorFactory->create($adapter, ['limit' => 2], [])->willReturn($paginator)->shouldBeCalled(); 236 | $paginator->getCurrentPageResults()->willReturn(['a', 'b'], ['c']); 237 | $paginator->hasNextPage()->willReturn(true, false); 238 | $paginator->nextPage()->shouldBeCalledTimes(1); 239 | 240 | $this->getAll(['limit' => 2])->shouldReturn(['a', 'b', 'c']); 241 | } 242 | 243 | function it_gets_all_resources_for_a_specific_uri_with_uri_parameters($adapter, $client, $adapterFactory, $paginatorFactory, PaginatorInterface $paginator) 244 | { 245 | $this->beConstructedWith($client, 'parentUri/{parentId}/uri', $adapterFactory, $paginatorFactory); 246 | $paginatorFactory->create($adapter, ['limit' => 100], ['parentId' => 1])->willReturn($paginator)->shouldBeCalled(); 247 | $paginator->getCurrentPageResults()->willReturn(['a', 'b', 'c']); 248 | $paginator->hasNextPage()->willReturn(false); 249 | 250 | $this->getAll([], ['parentId' => 1])->shouldReturn(['a', 'b', 'c']); 251 | } 252 | 253 | function it_creates_paginator_with_defined_limit($adapter, $paginatorFactory, PaginatorInterface $paginator) 254 | { 255 | $paginatorFactory->create($adapter, ['limit' => 15], [])->willReturn($paginator)->shouldBeCalled(); 256 | $this->createPaginator(['limit' => 15])->shouldReturn($paginator); 257 | } 258 | 259 | function it_creates_paginator_with_default_limit($adapter, $paginatorFactory, PaginatorInterface $paginator) 260 | { 261 | $paginatorFactory->create($adapter, ['limit' => 10], [])->willReturn($paginator)->shouldBeCalled(); 262 | $this->createPaginator()->shouldReturn($paginator); 263 | } 264 | 265 | function it_throws_exception_when_invalid_response_format_is_received($client, ResponseInterface $response) 266 | { 267 | $response->getHeader('Content-Type')->willReturn('application/xhtml+xml'); 268 | $response->getBody()->willReturn('body'); 269 | $response->getStatusCode()->willReturn(400); 270 | $client->get('uri/1')->willReturn($response)->shouldBeCalled(); 271 | 272 | $this->shouldThrow(new InvalidResponseFormatException('body', 400))->during('get', [1]); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /spec/Map/ArrayUriMapSpec.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class ArrayUriMapSpec extends ObjectBehavior 20 | { 21 | function let() 22 | { 23 | $this->beConstructedWith( 24 | [ 25 | 'product_variants' => 'products/{productId}/products_variants', 26 | 'taxons' => 'taxonomies/{taxonomyId}/taxons' 27 | ] 28 | ); 29 | } 30 | 31 | function it_is_initializable() 32 | { 33 | $this->shouldHaveType('Sylius\Api\Map\ArrayUriMap'); 34 | } 35 | 36 | function it_implements_uri_map_interface() 37 | { 38 | $this->shouldImplement('Sylius\Api\Map\UriMapInterface'); 39 | } 40 | 41 | function its_get_uri_method_accepts_only_string() 42 | { 43 | $this->shouldThrow('InvalidArgumentException')->during('getUri', [123]); 44 | $this->shouldThrow('InvalidArgumentException')->during('getUri', [1.23]); 45 | $this->shouldThrow('InvalidArgumentException')->during('getUri', [true]); 46 | $this->shouldThrow('InvalidArgumentException')->during('getUri', [array()]); 47 | $this->shouldThrow('InvalidArgumentException')->during('getUri', [new \stdClass()]); 48 | $this->shouldNotThrow('InvalidArgumentException')->during('getUri', ['string']); 49 | } 50 | 51 | function it_throws_exception_when_empty_string_given_to_get_uri_method() 52 | { 53 | $this->shouldThrow('InvalidArgumentException')->during('getUri', ['']); 54 | } 55 | 56 | function it_gets_uri_for_defined_resource() 57 | { 58 | $this->getUri('product_variants')->shouldReturn('products/{productId}/products_variants'); 59 | $this->getUri('taxons')->shouldReturn('taxonomies/{taxonomyId}/taxons'); 60 | } 61 | 62 | function it_gets_default_uri_for_undefined_resource() 63 | { 64 | $this->getUri('products')->shouldReturn('products'); 65 | $this->getUri('taxonomies')->shouldReturn('taxonomies'); 66 | } 67 | 68 | function it_throws_exception_when_uri_for_resource_is_undefined_and_allow_default_uri_option_is_false() 69 | { 70 | $this->beConstructedWith( 71 | [ 72 | 'product_variants' => 'products/{productId}/products_variants', 73 | 'taxons' => 'taxonomies/{taxonomyId}/taxons' 74 | ], 75 | false 76 | ); 77 | $this->getUri('taxons')->shouldReturn('taxonomies/{taxonomyId}/taxons'); 78 | $this->shouldThrow('InvalidArgumentException')->during('getUri', ['products']); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /spec/PaginatorSpec.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class PaginatorSpec extends ObjectBehavior 21 | { 22 | function let(AdapterInterface $adapter) 23 | { 24 | $this->beConstructedWith($adapter, ['limit' => 10], []); 25 | } 26 | 27 | function it_is_initializable() 28 | { 29 | $this->shouldHaveType('Sylius\Api\Paginator'); 30 | } 31 | 32 | function it_implements_paginator_interface() 33 | { 34 | $this->shouldImplement('Sylius\Api\PaginatorInterface'); 35 | } 36 | 37 | function it_has_limit_10_by_default($adapter) 38 | { 39 | $this->beConstructedWith($adapter); 40 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10], [])->willReturn(20); 41 | $adapter->getResults(['page' => 1, 'limit' => 10], []) 42 | ->willReturn(array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')); 43 | 44 | $this->getCurrentPageResults()->shouldHaveCount(10); 45 | } 46 | 47 | function its_limit_can_be_specified($adapter) 48 | { 49 | $this->beConstructedWith($adapter, ['limit' => 15]); 50 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 15], [])->willReturn(30); 51 | $adapter->getResults(['page' => 1, 'limit' => 15], []) 52 | ->willReturn(array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o')); 53 | 54 | $this->getCurrentPageResults()->shouldHaveCount(15); 55 | } 56 | 57 | function it_validates_that_limit_is_int($adapter) 58 | { 59 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$adapter, ['limit' => '1']]); 60 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$adapter, ['limit' => new \stdClass()]]); 61 | $this->shouldThrow('InvalidArgumentException')->during('__construct', [$adapter, ['limit' => 1.5]]); 62 | } 63 | 64 | function it_gets_current_page_results($adapter) 65 | { 66 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10], [])->willReturn(3); 67 | $adapter->getResults(['page' => 1, 'limit' => 10], [])->willReturn(array('a', 'b', 'c')); 68 | 69 | $this->getCurrentPageResults()->shouldReturn(array('a', 'b', 'c')); 70 | } 71 | 72 | function it_caches_results_for_current_page($adapter) 73 | { 74 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10],[])->willReturn(3); 75 | $adapter->getResults(['page' => 1, 'limit' => 10], [])->shouldBeCalledTimes(1); 76 | $adapter->getResults(['page' => 1, 'limit' => 10], [])->willReturn(array('a', 'b', 'c')); 77 | $this->getCurrentPageResults()->shouldReturn(array('a', 'b', 'c')); 78 | 79 | $adapter->getResults(['page' => 1, 'limit' => 10], [])->willReturn(array('d', 'e', 'f')); 80 | $this->getCurrentPageResults()->shouldReturn(array('a', 'b', 'c')); 81 | } 82 | 83 | function it_moves_to_the_next_page($adapter) 84 | { 85 | $this->beConstructedWith($adapter, ['limit' => 5]); 86 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 5], [])->willReturn(8); 87 | $adapter->getResults(['page' => 1, 'limit' => 5], [])->willReturn(array('a', 'b', 'c', 'b', 'e')); 88 | $adapter->getResults(['page' => 2, 'limit' => 5], [])->willReturn(array('f', 'g', 'h')); 89 | 90 | $this->getCurrentPageResults()->shouldReturn(array('a', 'b', 'c', 'b', 'e')); 91 | $this->nextPage(); 92 | $this->getCurrentPageResults()->shouldReturn(array('f', 'g', 'h')); 93 | } 94 | 95 | function it_moves_to_the_previous_page($adapter) 96 | { 97 | $this->beConstructedWith($adapter, ['limit' => 5]); 98 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 5], [])->willReturn(8); 99 | $adapter->getResults(['page' => 1, 'limit' => 5], [])->shouldBeCalledTimes(2); 100 | $adapter->getResults(['page' => 1, 'limit' => 5], [])->willReturn(array('a', 'b', 'c', 'b', 'e')); 101 | $adapter->getResults(['page' => 2, 'limit' => 5], [])->willReturn(array('f', 'g', 'h')); 102 | 103 | $this->getCurrentPageResults()->shouldReturn(array('a', 'b', 'c', 'b', 'e')); 104 | $this->nextPage(); 105 | $this->getCurrentPageResults()->shouldReturn(array('f', 'g', 'h')); 106 | $this->previousPage(); 107 | $this->getCurrentPageResults()->shouldReturn(array('a', 'b', 'c', 'b', 'e')); 108 | } 109 | 110 | function it_returns_false_if_there_is_no_previous_page() 111 | { 112 | $this->hasPreviousPage()->shouldReturn(false); 113 | } 114 | 115 | function it_throws_exception_when_can_not_move_to_the_previous_page() 116 | { 117 | $this->shouldThrow('LogicException')->during('previousPage', []); 118 | } 119 | 120 | function it_returns_true_if_there_is_previous_page($adapter) 121 | { 122 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10], [])->willReturn(25); 123 | 124 | $this->nextPage(); 125 | $this->hasPreviousPage()->shouldReturn(true); 126 | } 127 | 128 | function it_gets_number_of_results($adapter) 129 | { 130 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10], [])->willReturn(5); 131 | 132 | $this->getNumberOfResults()->shouldReturn(5); 133 | } 134 | 135 | function it_returns_false_if_there_is_no_next_page($adapter) 136 | { 137 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10], [])->willReturn(8); 138 | 139 | $this->hasNextPage()->shouldReturn(false); 140 | } 141 | 142 | function it_returns_true_if_there_is_next_page($adapter) 143 | { 144 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10], [])->willReturn(25); 145 | 146 | $this->hasNextPage()->shouldReturn(true); 147 | } 148 | 149 | function it_throws_exception_when_can_not_move_to_the_next_page($adapter) 150 | { 151 | $adapter->getNumberOfResults(['page' => 1, 'limit' => 10], [])->willReturn(8); 152 | 153 | $this->shouldThrow('LogicException')->during('nextPage', []); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/AdapterInterface.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | interface AdapterInterface 18 | { 19 | /** 20 | * @param array $queryParameters 21 | * @param array $uriParameters 22 | * 23 | * @return int 24 | */ 25 | public function getNumberOfResults(array $queryParameters, array $uriParameters = []); 26 | 27 | /** 28 | * @param array $queryParameters 29 | * @param array $uriParameters 30 | * 31 | * @return array 32 | */ 33 | public function getResults(array $queryParameters, array $uriParameters = []); 34 | } 35 | -------------------------------------------------------------------------------- /src/ApiAdapter.php: -------------------------------------------------------------------------------- 1 | api = $api; 20 | } 21 | 22 | public function getNumberOfResults(array $queryParameters, array $uriParameters = []) 23 | { 24 | $result = $this->getResult($queryParameters, $uriParameters, true); 25 | 26 | return isset($result['total']) ? $result['total'] : 0; 27 | } 28 | 29 | public function getResults(array $queryParameters, array $uriParameters = []) 30 | { 31 | $result = $this->getResult($queryParameters, $uriParameters); 32 | 33 | return isset($result['_embedded']['items']) ? $result['_embedded']['items'] : array(); 34 | } 35 | 36 | /** 37 | * @param array $queryParameters 38 | * @param array $uriParameters 39 | * @param bool $cacheResult 40 | * 41 | * @return array 42 | */ 43 | private function getResult(array $queryParameters, array $uriParameters, $cacheResult = false) 44 | { 45 | $hash = md5(serialize($queryParameters) . serialize($uriParameters)); 46 | if (isset($this->cachedResults[$hash])) { 47 | return $this->cachedResults[$hash]; 48 | } 49 | 50 | $result = $this->api->getPaginated($queryParameters, $uriParameters); 51 | if ($cacheResult) { 52 | $this->cachedResults = array(); 53 | $this->cachedResults[$hash] = $result; 54 | } 55 | 56 | return $result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/ApiInterface.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | interface ApiInterface 18 | { 19 | /** 20 | * @param string|int $id Resource ID 21 | * @param array $uriParameters 22 | * @return array 23 | */ 24 | public function get($id, array $uriParameters = []); 25 | 26 | /** 27 | * @param array $queryParameters 28 | * @param array $uriParameters 29 | * @return array 30 | */ 31 | public function getAll(array $queryParameters = [], array $uriParameters = []); 32 | 33 | /** 34 | * @param array $queryParameters 35 | * @param array $uriParameters 36 | * @return array 37 | */ 38 | public function getPaginated(array $queryParameters = [], array $uriParameters = []); 39 | 40 | /** 41 | * @param array $queryParameters 42 | * @param array $uriParameters 43 | * @return PaginatorInterface 44 | */ 45 | public function createPaginator(array $queryParameters = [], array $uriParameters = []); 46 | 47 | /** 48 | * @param array $body Array of fields to be sent to api 49 | * @param array $uriParameters 50 | * @param array $files Array of files to upload. Key = field key, Value = file path. 51 | * @return array 52 | */ 53 | public function create(array $body, array $uriParameters = [], array $files = []); 54 | 55 | /** 56 | * @param int $id Resource ID 57 | * @param array $body Array of fields to be sent to api 58 | * @param array $uriParameters 59 | * @param array $files Array of files to upload. Key = field key, Value = file path. 60 | * @return bool 61 | */ 62 | public function update($id, array $body, array $uriParameters = [], array $files = []); 63 | 64 | /** 65 | * @param string|int $id Resource ID 66 | * @param array $uriParameters 67 | * @return bool 68 | */ 69 | public function delete($id, array $uriParameters = []); 70 | } 71 | -------------------------------------------------------------------------------- /src/ApiResolver.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class ApiResolver implements ApiResolverInterface 20 | { 21 | /** 22 | * @var UriMapInterface 23 | */ 24 | private $uriMap; 25 | /** 26 | * @var ClientInterface 27 | */ 28 | private $client; 29 | 30 | public function __construct(ClientInterface $client, UriMapInterface $uriMap) 31 | { 32 | $this->client = $client; 33 | $this->uriMap = $uriMap; 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public function getApi($resource) 40 | { 41 | return new GenericApi($this->client, $this->uriMap->getUri($resource)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ApiResolverInterface.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | interface ApiResolverInterface 18 | { 19 | /** 20 | * @param string $resource Plural name of the resource 21 | * @return ApiInterface 22 | */ 23 | public function getApi($resource); 24 | } 25 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | class Client implements ClientInterface 29 | { 30 | /** 31 | * @var Url $baseUrl 32 | */ 33 | private $baseUrl; 34 | /** 35 | * @var HttpClientInterface $httpClient 36 | */ 37 | private $httpClient; 38 | /** 39 | * @var PostFileFactoryInterface $postFileFactory 40 | */ 41 | private $postFileFactory; 42 | 43 | public function __construct(HttpClientInterface $httpClient, PostFileFactoryInterface $postFileFactory = null) 44 | { 45 | $this->httpClient = $httpClient; 46 | $this->postFileFactory = $postFileFactory ?: new PostFileFactory(); 47 | $this->baseUrl = Url::fromString($httpClient->getBaseUrl()); 48 | } 49 | 50 | /** 51 | * {@inheritdoc } 52 | */ 53 | public function get($url, array $queryParameters = []) 54 | { 55 | return $this->httpClient->get($url, ['query' => $queryParameters]); 56 | } 57 | 58 | /** 59 | * {@inheritdoc } 60 | */ 61 | public function patch($url, array $body) 62 | { 63 | return $this->httpClient->patch($url, ['body' => $body]); 64 | } 65 | 66 | /** 67 | * {@inheritdoc } 68 | */ 69 | public function put($url, array $body) 70 | { 71 | return $this->httpClient->put($url, ['body' => $body]); 72 | } 73 | 74 | /** 75 | * {@inheritdoc } 76 | */ 77 | public function delete($url) 78 | { 79 | return $this->httpClient->delete($url); 80 | } 81 | 82 | /** 83 | * {@inheritdoc } 84 | */ 85 | public function post($url, $body, array $files = array()) 86 | { 87 | $request = $this->httpClient->createRequest('POST', $url, ['body' => $body]); 88 | /** @var PostBodyInterface $postBody */ 89 | $postBody = $request->getBody(); 90 | foreach ($files as $key => $filePath) { 91 | $file = $this->postFileFactory->create($key, $filePath); 92 | $postBody->addFile($file); 93 | } 94 | $response = $this->httpClient->send($request); 95 | 96 | return $response; 97 | } 98 | 99 | /** 100 | * {@inheritdoc } 101 | */ 102 | public function getSchemeAndHost() 103 | { 104 | return sprintf('%s://%s', $this->baseUrl->getScheme(), $this->baseUrl->getHost()); 105 | } 106 | 107 | /** 108 | * @param string $url 109 | * @param null|SubscriberInterface $subscriber 110 | * @param array $options 111 | * @return Client 112 | */ 113 | public static function createFromUrl($url, SubscriberInterface $subscriber = null, array $options = []) 114 | { 115 | $options['base_url'] = $url; 116 | self::resolveDefaults($options, $subscriber); 117 | 118 | return new self(new HttpClient($options)); 119 | } 120 | 121 | private static function resolveDefaults(array &$options, SubscriberInterface $subscriber = null) 122 | { 123 | $options['defaults']['headers']['User-Agent'] = isset($options['defaults']['headers']['User-Agent']) ? $options['defaults']['headers']['User-Agent'] : 'SyliusApi/0.1'; 124 | $options['defaults']['headers']['Accept'] = isset($options['defaults']['headers']['Accept']) ? $options['defaults']['headers']['Accept'] : 'application/json'; 125 | $options['defaults']['exceptions'] = isset($options['defaults']['exceptions']) ? $options['defaults']['exceptions'] : false; 126 | 127 | if ($subscriber) { 128 | $options['defaults']['subscribers'][] = $subscriber; 129 | if ($subscriber instanceof Oauth2Subscriber) { 130 | $options['defaults']['auth'] = 'oauth2'; 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/ClientInterface.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | interface ClientInterface 24 | { 25 | /** 26 | * @param string|Url $url URL or URI template 27 | * @param array $queryParameters 28 | * @return ResponseInterface 29 | */ 30 | public function get($url, array $queryParameters = []); 31 | 32 | /** 33 | * @param string|Url $url URL or URI template 34 | * @param array $body 35 | * @return ResponseInterface 36 | */ 37 | public function patch($url, array $body); 38 | 39 | /** 40 | * @param string|Url $url URL or URI template 41 | * @param array $body 42 | * @return ResponseInterface 43 | */ 44 | public function put($url, array $body); 45 | 46 | /** 47 | * @param string|Url $url URL or URI template 48 | * @return ResponseInterface 49 | */ 50 | public function delete($url); 51 | 52 | /** 53 | * @param string|Url $url URL or URI template 54 | * @param array $body 55 | * @param array $files 56 | * @return ResponseInterface 57 | */ 58 | public function post($url, $body, array $files = array()); 59 | 60 | /** 61 | * @return string The scheme and HTTP host 62 | */ 63 | public function getSchemeAndHost(); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/Factory/AdapterFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | interface AdapterFactoryInterface 20 | { 21 | /** 22 | * @return AdapterInterface 23 | */ 24 | public function create(); 25 | } 26 | -------------------------------------------------------------------------------- /src/Factory/ApiAdapterFactory.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class ApiAdapterFactory implements AdapterFactoryInterface 21 | { 22 | /** 23 | * @var ApiInterface $api 24 | */ 25 | private $api; 26 | 27 | public function __construct(ApiInterface $api) 28 | { 29 | $this->api = $api; 30 | } 31 | 32 | /** 33 | * {@inheritdoc } 34 | */ 35 | public function create() 36 | { 37 | return new ApiAdapter($this->api); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Factory/PaginatorFactory.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class PaginatorFactory implements PaginatorFactoryInterface 21 | { 22 | /** 23 | * {@inheritdoc } 24 | */ 25 | public function create(AdapterInterface $adapter, array $queryParameters, array $uriParameters = []) 26 | { 27 | return new Paginator($adapter, $queryParameters, $uriParameters); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Factory/PaginatorFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | interface PaginatorFactoryInterface 21 | { 22 | /** 23 | * @param AdapterInterface $adapter 24 | * @param array $queryParameters 25 | * @param array $uriParameters 26 | * @return PaginatorInterface 27 | */ 28 | public function create(AdapterInterface $adapter, array $queryParameters, array $uriParameters = []); 29 | } 30 | -------------------------------------------------------------------------------- /src/Factory/PostFileFactory.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class PostFileFactory implements PostFileFactoryInterface 22 | { 23 | /** 24 | * {@inheritdoc } 25 | */ 26 | public function create($key, $filePath) 27 | { 28 | return new PostFile($key, fopen($filePath, 'r')); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Factory/PostFileFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | interface PostFileFactoryInterface 22 | { 23 | /** 24 | * @param string $key 25 | * @param string $filePath 26 | * @return PostFileInterface 27 | */ 28 | public function create($key, $filePath); 29 | } 30 | -------------------------------------------------------------------------------- /src/GenericApi.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class GenericApi implements ApiInterface 24 | { 25 | /** 26 | * @var ClientInterface $client 27 | */ 28 | private $client; 29 | /** 30 | * @var string $uri 31 | */ 32 | private $uri; 33 | /** 34 | * @var PaginatorFactoryInterface $paginatorFactory 35 | */ 36 | private $paginatorFactory; 37 | 38 | /** 39 | * @var AdapterFactoryInterface $apiAdapterFactory 40 | */ 41 | private $apiAdapterFactory; 42 | 43 | /** 44 | * @param ClientInterface $client 45 | * @param string $uri 46 | * @param null|AdapterFactoryInterface $apiAdapterFactory 47 | * @param null|PaginatorFactoryInterface $paginatorFactory 48 | * @throws \InvalidArgumentException 49 | */ 50 | public function __construct(ClientInterface $client, $uri, AdapterFactoryInterface $apiAdapterFactory = null, PaginatorFactoryInterface $paginatorFactory = null) 51 | { 52 | $this->setUri($uri); 53 | $this->client = $client; 54 | $this->apiAdapterFactory = $apiAdapterFactory ?: new ApiAdapterFactory($this); 55 | $this->paginatorFactory = $paginatorFactory ?: new PaginatorFactory(); 56 | } 57 | 58 | /** 59 | * @param string $uri 60 | * @throws \InvalidArgumentException 61 | */ 62 | private function setUri($uri) 63 | { 64 | if (empty($uri) || !is_string($uri)) { 65 | throw new \InvalidArgumentException('You must specify uri for Api'); 66 | } 67 | if (($uri[strlen($uri) - 1]) !== '/') { 68 | $uri = sprintf('%s/', $uri); 69 | } 70 | $this->uri = $uri; 71 | } 72 | 73 | /** 74 | * @param array $uriParameters 75 | * @return string 76 | */ 77 | private function getUri(array $uriParameters = []) 78 | { 79 | $uri = $this->uri; 80 | foreach ($uriParameters as $uriParameterKey => $uriParameterValue) { 81 | $uri = str_ireplace(sprintf('{%s}', $uriParameterKey), $uriParameterValue, $uri); 82 | } 83 | 84 | return $uri; 85 | } 86 | 87 | /** 88 | * {@inheritdoc } 89 | */ 90 | public function get($id, array $uriParameters = []) 91 | { 92 | $response = $this->client->get(sprintf('%s%s', $this->getUri($uriParameters), $id)); 93 | 94 | return $this->responseToArray($response); 95 | } 96 | 97 | /** 98 | * {@inheritdoc } 99 | */ 100 | public function getAll(array $queryParameters = [], array $uriParameters = []) 101 | { 102 | $queryParameters['limit'] = isset($queryParameters['limit']) ? $queryParameters['limit'] : 100; 103 | $paginator = $this->createPaginator($queryParameters, $uriParameters); 104 | $results = $paginator->getCurrentPageResults(); 105 | while ($paginator->hasNextPage()) { 106 | $paginator->nextPage(); 107 | $results = array_merge($results, $paginator->getCurrentPageResults()); 108 | } 109 | 110 | return $results; 111 | } 112 | 113 | /** 114 | * {@inheritdoc } 115 | */ 116 | public function getPaginated(array $queryParameters = [], array $uriParameters = []) 117 | { 118 | $queryParameters['page'] = isset($queryParameters['page']) ? $queryParameters['page'] : 1; 119 | $queryParameters['limit'] = isset($queryParameters['limit']) ? $queryParameters['limit'] : 10; 120 | 121 | $response = $this->client->get($this->getUri($uriParameters), $queryParameters); 122 | 123 | return $this->responseToArray($response); 124 | } 125 | 126 | /** 127 | * {@inheritdoc } 128 | */ 129 | public function createPaginator(array $queryParameters = [], array $uriParameters = []) 130 | { 131 | $queryParameters['limit'] = isset($queryParameters['limit']) ? $queryParameters['limit'] : 10; 132 | return $this->paginatorFactory->create($this->apiAdapterFactory->create(), $queryParameters, $uriParameters); 133 | } 134 | 135 | /** 136 | * {@inheritdoc } 137 | */ 138 | public function create(array $body, array $uriParameters = [], array $files = []) 139 | { 140 | $response = $this->client->post($this->getUri($uriParameters), $body, $files); 141 | 142 | return $this->responseToArray($response); 143 | } 144 | 145 | /** 146 | * {@inheritdoc } 147 | */ 148 | public function update($id, array $body, array $uriParameters = [], array $files = []) 149 | { 150 | $uri = sprintf('%s%s', $this->getUri($uriParameters), $id); 151 | if (empty($files)) { 152 | $response = $this->client->patch($uri, $body); 153 | } else { 154 | $response = $this->client->post($uri, $body, $files); 155 | } 156 | 157 | return (204 === $response->getStatusCode()); 158 | } 159 | 160 | /** 161 | * {@inheritdoc } 162 | */ 163 | public function delete($id, array $uriParameters = []) 164 | { 165 | $response = $this->client->delete(sprintf('%s%s', $this->getUri($uriParameters), $id)); 166 | 167 | return (204 === $response->getStatusCode()); 168 | } 169 | 170 | private function responseToArray(ResponseInterface $response) 171 | { 172 | $responseType = $response->getHeader('Content-Type'); 173 | if ((false === strpos($responseType, 'application/json')) && (false === strpos($responseType, 'application/xml'))) { 174 | throw new InvalidResponseFormatException((string) $response->getBody(), $response->getStatusCode()); 175 | } 176 | 177 | return (strpos($responseType, 'application/json') !== false) ? $response->json() : $response->xml(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/InvalidResponseFormatException.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class InvalidResponseFormatException extends \Exception 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/Map/ArrayUriMap.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class ArrayUriMap implements UriMapInterface 18 | { 19 | /** 20 | * @var array $uriMapping 21 | */ 22 | private $uriMapping; 23 | /** 24 | * @var bool $allowDefaultUris 25 | */ 26 | private $allowDefaultUris; 27 | 28 | /** 29 | * @param array $uriMapping 30 | * @param bool $allowDefaultUris 31 | */ 32 | public function __construct(array $uriMapping, $allowDefaultUris = true) 33 | { 34 | $this->uriMapping = $uriMapping; 35 | $this->allowDefaultUris = $allowDefaultUris; 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function getUri($resource) 42 | { 43 | if (empty($resource) || !is_string($resource)) { 44 | throw new \InvalidArgumentException('The resource has to be string and cannot be empty.'); 45 | } 46 | if (isset($this->uriMapping[$resource])) { 47 | return $this->uriMapping[$resource]; 48 | } 49 | if ($this->allowDefaultUris) { 50 | return $resource; 51 | } 52 | throw new \InvalidArgumentException('No mapping defined for a given resource.'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Map/UriMapInterface.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | 18 | interface UriMapInterface 19 | { 20 | /** 21 | * @param string $resource 22 | * @return string Uri for given resource 23 | */ 24 | public function getUri($resource); 25 | } 26 | -------------------------------------------------------------------------------- /src/Paginator.php: -------------------------------------------------------------------------------- 1 | adapter = $adapter; 48 | $this->queryParameters = $queryParameters; 49 | $this->queryParameters['page'] = $this->currentPage; 50 | $this->uriParameters = $uriParameters; 51 | $this->lastPage = (int) ceil($this->getNumberOfResults() / $queryParameters['limit']); 52 | } 53 | 54 | public function getCurrentPageResults() 55 | { 56 | if (!$this->isResultCached()) { 57 | $this->queryParameters['page'] = $this->currentPage; 58 | $this->currentResults = $this->adapter->getResults($this->queryParameters, $this->uriParameters); 59 | } 60 | 61 | return $this->currentResults; 62 | } 63 | 64 | private function isResultCached() 65 | { 66 | return (null !== $this->currentResults); 67 | } 68 | 69 | public function previousPage() 70 | { 71 | if (!$this->hasPreviousPage()) { 72 | throw new \LogicException('There is no previous page.'); 73 | } 74 | $this->currentPage--; 75 | $this->currentResults = null; 76 | } 77 | 78 | public function nextPage() 79 | { 80 | if (!$this->hasNextPage()) { 81 | throw new \LogicException('There is no next page.'); 82 | } 83 | $this->currentPage++; 84 | $this->currentResults = null; 85 | } 86 | 87 | public function hasPreviousPage() 88 | { 89 | return (1 < $this->currentPage); 90 | } 91 | 92 | public function hasNextPage() 93 | { 94 | return ($this->currentPage < $this->lastPage); 95 | } 96 | 97 | public function getNumberOfResults() 98 | { 99 | if (-1 === $this->numberOfResults) { 100 | $this->numberOfResults = $this->adapter->getNumberOfResults($this->queryParameters, $this->uriParameters); 101 | } 102 | 103 | return $this->numberOfResults; 104 | } 105 | 106 | /** 107 | * @return int 108 | */ 109 | public function getCurrentPage() 110 | { 111 | return $this->currentPage; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/PaginatorInterface.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | interface PaginatorInterface 18 | { 19 | /** 20 | * @return int 21 | */ 22 | public function getNumberOfResults(); 23 | 24 | /** 25 | * @return array 26 | */ 27 | public function getCurrentPageResults(); 28 | 29 | /** 30 | * @return int 31 | */ 32 | public function getCurrentPage(); 33 | 34 | /** 35 | * Moves to the next page 36 | * @return void 37 | */ 38 | public function nextPage(); 39 | 40 | /** 41 | * @return bool 42 | */ 43 | public function hasNextPage(); 44 | 45 | /** 46 | * Moves to the previous page 47 | * @return void 48 | */ 49 | public function previousPage(); 50 | 51 | /** 52 | * @return bool 53 | */ 54 | public function hasPreviousPage(); 55 | } 56 | --------------------------------------------------------------------------------