├── .php-cs-fixer.php ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── art └── logo.png ├── composer.json ├── config └── prestashop.php └── src ├── Facades └── Prestashop.php ├── LaravelPrestashopServiceProvider.php ├── Models └── Resource.php └── Prestashop.php /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | in([ 5 | __DIR__ . '/src', 6 | __DIR__ . '/tests', 7 | ]) 8 | ->name('*.php') 9 | ->notName('*.blade.php') 10 | ->ignoreDotFiles(true) 11 | ->ignoreVCS(true); 12 | 13 | return (new PhpCsFixer\Config()) 14 | ->setRules([ 15 | '@PSR2' => true, 16 | 'array_syntax' => ['syntax' => 'short'], 17 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 18 | 'no_unused_imports' => true, 19 | 'not_operator_with_successor_space' => true, 20 | 'trailing_comma_in_multiline' => true, 21 | 'phpdoc_scalar' => true, 22 | 'unary_operator_spaces' => true, 23 | 'binary_operator_spaces' => true, 24 | 'blank_line_before_statement' => [ 25 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 26 | ], 27 | 'phpdoc_single_line_var_spacing' => true, 28 | 'phpdoc_var_without_name' => true, 29 | 'class_attributes_separation' => [ 30 | 'elements' => [ 31 | 'method' => 'one', 32 | ], 33 | ], 34 | 'method_argument_space' => [ 35 | 'on_multiline' => 'ensure_fully_multiline', 36 | 'keep_multiple_spaces_after_comma' => true, 37 | ], 38 | 'single_trait_insert_per_statement' => true, 39 | ]) 40 | ->setFinder($finder); 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-prestashop` will be documented in this file. 4 | 5 | ## 1.0.0 - 2021-07-08 6 | 7 | - First beta release 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) lucasgiovanny 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 | 2 | Laravel Prestashop 3 | 4 | This package still in development. Feel free to contribute and give ideas! 5 | 6 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/lucasgiovanny/laravel-prestashop?label=last%20version) 7 | ![GitHub](https://img.shields.io/github/license/lucasgiovanny/laravel-prestashop) 8 | 9 | ## Documentation 10 | 11 | Access the app [documentation](https://laravel-prestashop.lucasgiovanny.com/docs). 12 | 13 | ## Testing 14 | 15 | On the todo list. 16 | 17 | ## Changelog 18 | 19 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 20 | 21 | ## Contributing 22 | 23 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 24 | 25 | ## Security Vulnerabilities 26 | 27 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 28 | 29 | ## Credits 30 | 31 | - [Lucas Giovanny](https://github.com/lucasgiovanny) 32 | - [All Contributors](../../contributors) 33 | 34 | ## License 35 | 36 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 37 | -------------------------------------------------------------------------------- /art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasgiovanny/laravel-prestashop/5ad014628f5f1d97fbef700164bcae0b928885ca/art/logo.png -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lucasgiovanny/laravel-prestashop", 3 | "description": "Package for use PrestaShop Webservice on Laravel.", 4 | "keywords": [ 5 | "lucasgiovanny", 6 | "laravel", 7 | "laravel-prestashop" 8 | ], 9 | "homepage": "https://github.com/lucasgiovanny/laravel-prestashop", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Lucas Giovanny", 14 | "email": "lucasgiovanny@gmail.com", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^7.4|^8.0", 20 | "guzzlehttp/guzzle": "^7.0.1", 21 | "illuminate/contracts": "^8.37|^9.0", 22 | "spatie/laravel-package-tools": "^1.4.3" 23 | }, 24 | "require-dev": { 25 | "brianium/paratest": "^6.2", 26 | "nunomaduro/collision": "^5.3", 27 | "orchestra/testbench": "^6.15", 28 | "phpunit/phpunit": "^9.3", 29 | "spatie/laravel-ray": "^1.9", 30 | "vimeo/psalm": "^4.4" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Lucasgiovanny\\LaravelPrestashop\\": "src", 35 | "Lucasgiovanny\\LaravelPrestashop\\Database\\Factories\\": "database/factories" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Lucasgiovanny\\LaravelPrestashop\\Tests\\": "tests" 41 | } 42 | }, 43 | "scripts": { 44 | "psalm": "vendor/bin/psalm", 45 | "test": "./vendor/bin/testbench package:test --parallel --no-coverage", 46 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 47 | }, 48 | "config": { 49 | "sort-packages": true 50 | }, 51 | "extra": { 52 | "laravel": { 53 | "providers": [ 54 | "Lucasgiovanny\\LaravelPrestashop\\LaravelPrestashopServiceProvider" 55 | ], 56 | "aliases": { 57 | "LaravelPrestashop": "Lucasgiovanny\\LaravelPrestashop\\LaravelPrestashopFacade" 58 | } 59 | } 60 | }, 61 | "minimum-stability": "dev", 62 | "prefer-stable": true 63 | } 64 | -------------------------------------------------------------------------------- /config/prestashop.php: -------------------------------------------------------------------------------- 1 | [ 14 | 'endpoint' => env('PRESTASHOP_ENDPOINT'), 15 | 'token' => env('PRESTASHOP_TOKEN'), 16 | ], 17 | 18 | ]; 19 | -------------------------------------------------------------------------------- /src/Facades/Prestashop.php: -------------------------------------------------------------------------------- 1 | name('laravel-prestashop') 19 | ->hasConfigFile(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Models/Resource.php: -------------------------------------------------------------------------------- 1 | attributes[$name] ?? null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Prestashop.php: -------------------------------------------------------------------------------- 1 | 'JSON', 164 | 'Output-Format' => 'JSON', 165 | ]; 166 | 167 | /** 168 | * Construct the class with dependencies 169 | * 170 | * @param HttpClient $http 171 | * 172 | * @return void 173 | */ 174 | public function __construct(protected HttpClient $http) 175 | { 176 | } 177 | 178 | /** 179 | * Configure the Prestashop store 180 | * 181 | * @param string $endpoint 182 | * @param string $token 183 | * @param int $shop 184 | * 185 | * @return $this 186 | */ 187 | public function shop(string $endpoint, string $token, int $shop = null) 188 | { 189 | $this->endpoint = $endpoint; 190 | $this->token = $token; 191 | $this->shop = $shop; 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * Configure the Prestashop store 198 | * 199 | * @param string $endpoint 200 | * @param string $token 201 | * @param int $shop 202 | * 203 | * @return $this 204 | */ 205 | public function store(string $endpoint, string $token, int $shop = null) 206 | { 207 | $this->shop($endpoint, $token, $shop); 208 | 209 | return $this; 210 | } 211 | 212 | /** 213 | * Set the resource to be used 214 | * 215 | * @param string $resource 216 | * @param mixed ...$arguments 217 | * 218 | * @return $this 219 | */ 220 | public function resource(string $resource, ...$arguments) 221 | { 222 | $this->resource = $resource; 223 | 224 | return $this; 225 | } 226 | 227 | /** 228 | * Define the request limit or index and limit 229 | * 230 | * @param int $limit 231 | * @param int $index 232 | * 233 | * @return $this 234 | */ 235 | public function limit(int $limit, int $index = null) 236 | { 237 | $this->limit = $index ? [$index, $limit] : $limit; 238 | 239 | return $this; 240 | } 241 | 242 | /** 243 | * Add sort fields by ASC 244 | * 245 | * @param string $field 246 | * @param string $order 247 | * 248 | * @return $this 249 | */ 250 | protected function sort(string $field, string $order) 251 | { 252 | $this->sort[] = [ 253 | 'value' => $field, 254 | 'order' => $order, 255 | ]; 256 | 257 | return $this; 258 | } 259 | 260 | /** 261 | * Add sort fields by DESC 262 | * 263 | * @param string $field 264 | * 265 | * @return $this 266 | */ 267 | public function sortBy(string $field) 268 | { 269 | $this->sort($field, "ASC"); 270 | 271 | return $this; 272 | } 273 | 274 | /** 275 | * Add sort fields by DESC 276 | * 277 | * @param string $field 278 | * 279 | * @return $this 280 | */ 281 | public function sortByDesc(string $field) 282 | { 283 | $this->sort($field, "DESC"); 284 | 285 | return $this; 286 | } 287 | 288 | /** 289 | * Alias for sortBy 290 | * 291 | * @param string $field 292 | * 293 | * @return $this 294 | */ 295 | public function orderBy(string $field) 296 | { 297 | $this->sort($field, "ASC"); 298 | 299 | return $this; 300 | } 301 | 302 | /** 303 | * Alias for sortByDesc 304 | * 305 | * @param string $field 306 | * 307 | * @return $this 308 | */ 309 | public function orderByDesc(string $field) 310 | { 311 | $this->sort($field, "DESC"); 312 | 313 | return $this; 314 | } 315 | 316 | /** 317 | * Shortcut for display method 318 | * 319 | * @param string|array $fields 320 | * 321 | * @return $this 322 | */ 323 | public function select($fields) 324 | { 325 | return $this->display($fields); 326 | } 327 | 328 | /** 329 | * Select fields to be returned by web service 330 | * 331 | * @param string|array $fields 332 | * 333 | * @return $this 334 | */ 335 | public function display($fields) 336 | { 337 | $this->display = is_array($fields) ? $fields : [$fields]; 338 | 339 | return $this; 340 | } 341 | 342 | /** 343 | * Add a filter to the web service call 344 | * 345 | * @param string $field 346 | * @param string $operatorOrValue 347 | * @param string|array $value 348 | * 349 | * @return $this 350 | */ 351 | public function filter(string $field, string $operatorOrValue, $value = null) 352 | { 353 | $operator = $value ? $operatorOrValue : '='; 354 | 355 | if (! in_array(strtoupper($operator), self::FILTER_OPERATORS)) { 356 | throw new Exception('Invalid filter operator'); 357 | } 358 | 359 | $this->filters[] = [ 360 | 'field' => $field, 361 | 'operator' => strtoupper($operator), 362 | 'value' => $value ?: $operatorOrValue, 363 | ]; 364 | 365 | return $this; 366 | } 367 | 368 | /** 369 | * Shortcut to filter method 370 | * 371 | * @param string $field 372 | * @param string $operatorOrValue 373 | * @param string|array $value 374 | * 375 | * @return $this 376 | */ 377 | public function where(string $field, string $operatorOrValue, $value = null) 378 | { 379 | return $this->filter($field, $operatorOrValue, $value); 380 | } 381 | 382 | /** 383 | * Execute the get request 384 | * 385 | * @return \Illuminate\Support\Collection 386 | */ 387 | public function get() 388 | { 389 | return $this->call("get"); 390 | } 391 | 392 | /** 393 | * Execute the get request and return first result 394 | * 395 | * @return \Illuminate\Support\Collection|null 396 | */ 397 | public function first() 398 | { 399 | $get = $this->call("get"); 400 | 401 | return $get->isNotEmpty() ? $get->first() : null; 402 | } 403 | 404 | /** 405 | * Execute the get request with the condition applied 406 | * 407 | * @param int $id 408 | * 409 | * @return \Illuminate\Support\Collection|null 410 | */ 411 | public function find(int $id) 412 | { 413 | if ($this->filters) { 414 | throw new Exception("You can not use find method along with filters"); 415 | } 416 | 417 | $this->filters = [ 418 | [ 419 | 'field' => 'id', 420 | 'operator' => '=', 421 | 'value' => $id, 422 | ], 423 | ]; 424 | 425 | $get = $this->call("get"); 426 | 427 | return $get->isNotEmpty() ? $get->first() : null; 428 | } 429 | 430 | /** 431 | * Internal method to make the correct request call 432 | * 433 | * @param string $method 434 | * 435 | * @return \Illuminate\Support\Collection 436 | * 437 | * @throws Exception 438 | */ 439 | protected function call(string $method) 440 | { 441 | $this->method = in_array($method, ["get", "post", "put", "delete"]) ? $method : null; 442 | 443 | if ($this->canExecute()) { 444 | return $this->response($this->exec()); 445 | } 446 | 447 | throw new Exception("Error occur when trying to execute the API call"); 448 | } 449 | 450 | /** 451 | * Check if the request can be executed 452 | * 453 | * @return bool 454 | * 455 | * @throws Exception 456 | */ 457 | protected function canExecute() 458 | { 459 | if (! $this->resource) { 460 | throw new Exception("You need to define a resource."); 461 | } 462 | 463 | if (! $this->method) { 464 | throw new Exception("You need to define a method."); 465 | } 466 | 467 | if (! $this->url()) { 468 | throw new Exception("No endpoint/URL defined."); 469 | } 470 | 471 | if (! $this->token()) { 472 | throw new Exception("No token defined."); 473 | } 474 | 475 | return true; 476 | } 477 | 478 | /** 479 | * Execute the request to Prestashop web service 480 | * 481 | * @return array 482 | */ 483 | protected function exec() 484 | { 485 | $url = trim($this->url(), "/") . "/" . trim($this->resource, "/"); 486 | 487 | 488 | $res = $this->http->request( 489 | strtoupper($this->method), 490 | $url, 491 | [ 492 | RequestOptions::AUTH => [$this->token(), null], 493 | RequestOptions::HEADERS => $this->headers, 494 | RequestOptions::QUERY => $this->query(), 495 | ] 496 | ); 497 | 498 | return $res->getBody() ? json_decode($res->getBody(), true) : null; 499 | } 500 | 501 | /** 502 | * Prepare query for request 503 | * 504 | * @return array 505 | */ 506 | protected function query() 507 | { 508 | $query = [ 509 | 'display' => $this->display ? 510 | "[" . implode(",", $this->display) . "]" : 'full', 511 | ]; 512 | 513 | if ($this->limit) { 514 | $query['limit'] = is_array($this->limit) ? 515 | "{$this->limit[0]}, {$this->limit[1]}" 516 | : $this->limit; 517 | } 518 | 519 | if ($this->filters) { 520 | foreach ($this->filters as $filter) { 521 | if ($filter['operator'] === "|" || $filter['operator'] === "OR") { 522 | $value = "[" . implode("|", $filter['value']) . "]"; 523 | } 524 | 525 | if ($filter['operator'] === "," || $filter['operator'] === "INTERVAL") { 526 | $value = "[" . implode(",", $filter['value']) . "]"; 527 | } 528 | 529 | if ($filter['operator'] === "=" || $filter['operator'] === "LITERAL") { 530 | $value = "[" . $filter['value'] . "]"; 531 | } 532 | 533 | if ($filter['operator'] === "BEGIN") { 534 | $value = "[" . $filter['value'] . "]%"; 535 | } 536 | 537 | if ($filter['operator'] === "END") { 538 | $value = "%[" . $filter['value'] . "]"; 539 | } 540 | 541 | if ($filter['operator'] === "CONTAINS") { 542 | $value = "%[" . $filter['value'] . "]%"; 543 | } 544 | 545 | $query["filter[" . $filter['field'] . "]"] = $value; 546 | 547 | if(Str::contains($filter['field'], 'date')){ 548 | $query['date'] = 1; 549 | } 550 | } 551 | } 552 | 553 | if ($this->sort) { 554 | foreach ($this->sort as $sort) { 555 | $sortQuery[] = "{$sort['value']}_{$sort['order']}"; 556 | } 557 | 558 | $query['sort'] = "[" . implode(",", $sortQuery) . "]"; 559 | } 560 | 561 | if ($this->shop) { 562 | $query['id_shop'] = $this->shop; 563 | } 564 | 565 | return $query; 566 | } 567 | 568 | /** 569 | * Define the endpoint for the request 570 | * 571 | * @return string 572 | */ 573 | protected function token() 574 | { 575 | return $this->token ?: config('prestashop.shop.token'); 576 | } 577 | 578 | /** 579 | * Define the endpoint for the request 580 | * 581 | * @return string 582 | */ 583 | protected function url() 584 | { 585 | return $this->endpoint ?: config('prestashop.shop.endpoint'); 586 | } 587 | 588 | /** 589 | * Format and delivery the response as Laravel Collection 590 | * 591 | * @param array $response 592 | * 593 | * @return \Illuminate\Support\Collection 594 | * 595 | * @throws Exception 596 | */ 597 | protected function response(?array $response) 598 | { 599 | if (! $response) { 600 | throw new Exception("No response from server"); 601 | } 602 | 603 | $response = $response[$this->resource] ?? $response; 604 | 605 | foreach ($response as $element) { 606 | $data[] = new Resource($this->resource, $element); 607 | } 608 | 609 | return collect($data ?? null); 610 | } 611 | 612 | /** 613 | * Create the method for each web service resource 614 | * 615 | * @param string $method 616 | * @param array $arguments 617 | * 618 | * @return mixed 619 | */ 620 | public function __call(string $method, array $arguments) 621 | { 622 | if (in_array($method, self::RESOURCES)) { 623 | return $this->resource($method, $arguments); 624 | } 625 | 626 | throw new Exception("This is not a valid resource"); 627 | } 628 | } 629 | --------------------------------------------------------------------------------