├── .php-cs-fixer.dist.php ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── strapi.php ├── grumphp.yml.dist ├── rector.php └── src ├── Commands └── LaravelStrapiCommand.php ├── Exceptions ├── NotFound.php ├── PermissionDenied.php └── UnknownError.php ├── LaravelStrapi.php ├── LaravelStrapiFacade.php └── LaravelStrapiServiceProvider.php /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in([__DIR__]) 33 | ->exclude(['vendor']) 34 | ->append([__DIR__.'/.php-cs-fixer.dist.php']) 35 | ; 36 | 37 | $config = (new Config()) 38 | ->setCacheFile(sys_get_temp_dir().'/.php_cs.cache') 39 | ->setRiskyAllowed(true) 40 | ->setRules([ 41 | // https://mlocati.github.io/php-cs-fixer-configurator 42 | '@PHP74Migration:risky' => true, 43 | '@PHP74Migration' => true, 44 | '@PhpCsFixer' => true, 45 | // '@PhpCsFixer:risky' => true, 46 | 'general_phpdoc_annotation_remove' => ['annotations' => ['expectedDeprecation']], 47 | 'header_comment' => ['header' => $header], 48 | ]) 49 | ->setFinder($finder) 50 | ; 51 | 52 | // special handling of fabbot.io service if it's using too old PHP CS Fixer version 53 | if (false !== getenv('FABBOT_IO')) { 54 | try { 55 | FixerFactory::create() 56 | ->registerBuiltInFixers() 57 | ->registerCustomFixers($config->getCustomFixers()) 58 | ->useRuleSet(new RuleSet($config->getRules())) 59 | ; 60 | } catch (InvalidConfigurationException $e) { 61 | $config->setRules([]); 62 | } catch (UnexpectedValueException $e) { 63 | $config->setRules([]); 64 | } catch (InvalidArgumentException $e) { 65 | $config->setRules([]); 66 | } 67 | } 68 | 69 | return $config; 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-strapi` will be documented in this file. 4 | 5 | ## 4.0.1 - 2024-02-05 6 | 7 | - Code refactoring, support for PHP 7.4 8 | 9 | ## 4.0.0 - 2024-02-04 10 | 11 | - Updates to latest Laravel 12 | - Filter added to collection method 13 | 14 | ## 3.0.0 - 2022-11-07 15 | 16 | - [BREAKING] Support for Strapi v4 17 | 18 | ## 2.0.2 - 2022-06-13 19 | 20 | - Fix for issue https://github.com/dbfx/laravel-strapi/issues/16 with ['data'] response in entry() 21 | 22 | ## 2.0.0 - 2021-03-09 23 | 24 | - Introduction of authentication, requires new strapi.php config update for token => STRAPI_TOKEN 25 | 26 | ## 1.2.0 - 2021-03-08 27 | 28 | - Laravel 9 support 29 | 30 | ## 1.1.1 - 2021-12-21 31 | 32 | - Fix recursive full image urls 33 | 34 | ## 1.1.0 - 2021-12-21 35 | 36 | - Cleaned up the code base a little 37 | - Should be more reliable now at converting to full URLs without errors 38 | 39 | ## 1.0.11 - 2021-10-02 40 | 41 | - Fix for another array to string issue 42 | 43 | ## 1.0.9 - 2021-09-13 44 | 45 | - Fixes another problem with an array to string conversion error on single entries 46 | 47 | ## 1.0.8 - 2021-09-10 48 | 49 | - Fixes a problem with an array to string conversion error on collections 50 | 51 | ## 1.0.7 - 2021-05-23 52 | 53 | - Fixed a bug with forgetting collection caches 54 | 55 | ## 1.0.6 - 2021-04-29 56 | 57 | - Added limit and start options to collections 58 | 59 | ## 1.0.5 - 2021-04-28 60 | 61 | - Add the ability to sort and order collections by keys 62 | 63 | ## 1.0.4 - 2021-04-28 64 | 65 | - Fixed a bug with cache times 66 | 67 | ## 1.0.3 - 2021-04-28 68 | 69 | - Added entriesByField($type, $fieldName, $fieldValue) 70 | 71 | ## 1.0.2 - 2021-04-28 72 | 73 | - Added collectionCount($type) method 74 | - Added exceptions for errors 75 | - Added single($type) to get a single item, optionally with $pluck to fetch a single value 76 | 77 | ## 1.0.1 - 2021-04-27 78 | 79 | - fix an issue with caching specific entries 80 | 81 | ## 1.0.0 - 2021-04-26 82 | 83 | - initial release 84 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | dave@blakey.co. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) dbfx 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 | # Laravel wrapper for using the Strapi headless CMS 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/dbfx/laravel-strapi.svg?style=flat-square)](https://packagist.org/packages/dbfx/laravel-strapi) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/dbfx/laravel-strapi.svg?style=flat-square)](https://packagist.org/packages/dbfx/laravel-strapi) 5 | 6 | --- 7 | 8 | Laravel-Strapi is a Laravel helper for using the Strapi headless CMS. 9 | 10 | Note: for Strapi v3 support use version 2.x.x 11 | 12 | ## Installation 13 | 14 | You can install the package via composer: 15 | 16 | ```bash 17 | composer require dbfx/laravel-strapi 18 | ``` 19 | 20 | You can publish and run the migrations with: 21 | 22 | You can publish the config file with: 23 | ```bash 24 | php artisan vendor:publish --provider="Dbfx\LaravelStrapi\LaravelStrapiServiceProvider" --tag="strapi-config" 25 | ``` 26 | 27 | You need to define your STRAPI_URL and STRAPI_CACHE_TIME in .env: 28 | You can also optionally define a STRAPI_TOKEN to enable authentication. Do not include 'Bearer' only the token itself. 29 | 30 | ``` 31 | STRAPI_URL=https://strapi.test.com 32 | STRAPI_CACHE_TIME=3600 33 | STRAPI_TOKEN=abcd1234abcd1234 34 | ``` 35 | 36 | ## Usage 37 | 38 | laravel-strapi provides the collection() and entry() calls to return a full collection, or a specific entry from a collection. In the 39 | example below we are querying the strapi collection 'blogs' and then getting the entry with id 1 from that collection. 40 | ```php 41 | use Dbfx\LaravelStrapi\LaravelStrapi; 42 | 43 | $strapi = new LaravelStrapi(); 44 | $blogs = $strapi->collection('blogs'); 45 | $entry = $strapi->entry('blogs', 1); 46 | ``` 47 | 48 | There are several useful options available as well. 49 | 50 | - ```$sortKey``` and ```$sortOrder``` allow you to specify the key to sort on and the direction 51 | - ```$fullUrls``` will automatically add your STRAPI_URL to the front of any relative URLs (e.g. images, etc). 52 | - ```$limit``` sets how many items you are requesting 53 | - ```$start``` is the offset to be used with limit, useful for pagination 54 | - ```$populate``` is an array containing the fields to populate 55 | - ```$queryData``` is an array of additional key-value pairs to add to the query string 56 | 57 | ```php 58 | use Dbfx\LaravelStrapi\LaravelStrapi; 59 | 60 | $strapi = new LaravelStrapi(); 61 | $blogs = $strapi->collection('blogs', $sortKey = 'id', $sortOrder = 'DESC', $limit = 20, $start = 0, $fullUrls = true, $populate = ['author', 'images'], $queryData = ['locale' => 'en']); 62 | 63 | $entry = $strapi->entry('blogs', 1, $fullUrls = true, $populate = ['author', 'images'], $queryData = ['locale' => 'en']); 64 | ``` 65 | 66 | You may also access Single Type items as follows: 67 | 68 | ```php 69 | use Dbfx\LaravelStrapi\LaravelStrapi; 70 | 71 | $strapi = new LaravelStrapi(); 72 | 73 | // Fetch the full homepage array 74 | $homepageArray = $strapi->single('homepage'); 75 | 76 | // Return just the ['content'] field from the homepage array 77 | $homepageItem = $strapi->single('homepage', 'content'); 78 | ``` 79 | 80 | And you may select entries by searching for a custom field (e.g. slug): 81 | 82 | ```php 83 | use Dbfx\LaravelStrapi\LaravelStrapi; 84 | 85 | $strapi = new LaravelStrapi(); 86 | 87 | $entries = $strapi->entriesByField('blogs', 'slug', 'test-blog-post'); 88 | ``` 89 | 90 | ## Changelog 91 | 92 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 93 | 94 | ## Contributing 95 | 96 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 97 | 98 | ## Credits 99 | 100 | - [Dave Blakey](https://github.com/dbfx) 101 | - [All Contributors](../../contributors) 102 | 103 | ## License 104 | 105 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 106 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dbfx/laravel-strapi", 3 | "description": "Laravel wrapper for using the Strapi headless CMS", 4 | "license": "MIT", 5 | "keywords": [ 6 | "laravel", 7 | "strapi", 8 | "laravel-strapi", 9 | "strapi-cms", 10 | "laravel-package" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Dave Blakey", 15 | "email": "dave@blakey.co", 16 | "role": "Developer" 17 | } 18 | ], 19 | "homepage": "https://github.com/dbfx/laravel-strapi", 20 | "require": { 21 | "php": "^8.0", 22 | "illuminate/contracts": "^8.37 || ^9.0 || ^10", 23 | "laravel/framework": "^8 || ^9 || ^10", 24 | "spatie/laravel-package-tools": "^1.4.3" 25 | }, 26 | "require-dev": { 27 | "brianium/paratest": "^6.2", 28 | "ergebnis/composer-normalize": "^2.42", 29 | "friendsofphp/php-cs-fixer": "^3.48", 30 | "nunomaduro/collision": "^5.3 || ^6.0", 31 | "orchestra/testbench": "^6.15", 32 | "phpro/grumphp-shim": "^1.13", 33 | "phpunit/phpunit": "^9.3", 34 | "rector/rector": "^0.19.5", 35 | "roave/security-advisories": "dev-latest", 36 | "spatie/laravel-ray": "^1.9", 37 | "vimeo/psalm": "^5.7" 38 | }, 39 | "minimum-stability": "dev", 40 | "prefer-stable": true, 41 | "autoload": { 42 | "psr-4": { 43 | "Dbfx\\LaravelStrapi\\": "src", 44 | "Dbfx\\LaravelStrapi\\Database\\Factories\\": "database/factories" 45 | } 46 | }, 47 | "autoload-dev": { 48 | "psr-4": { 49 | "Dbfx\\LaravelStrapi\\Tests\\": "tests" 50 | } 51 | }, 52 | "config": { 53 | "allow-plugins": { 54 | "ergebnis/composer-normalize": true, 55 | "phpro/grumphp-shim": true 56 | }, 57 | "sort-packages": true 58 | }, 59 | "extra": { 60 | "laravel": { 61 | "aliases": { 62 | "LaravelStrapi": "Dbfx\\LaravelStrapi\\LaravelStrapiFacade" 63 | }, 64 | "providers": [ 65 | "Dbfx\\LaravelStrapi\\LaravelStrapiServiceProvider" 66 | ] 67 | } 68 | }, 69 | "scripts": { 70 | "psalm": "vendor/bin/psalm", 71 | "test": "./vendor/bin/testbench package:test --parallel --no-coverage", 72 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /config/strapi.php: -------------------------------------------------------------------------------- 1 | env('STRAPI_URL'), 17 | 18 | // How long to cache results for in seconds 19 | 'cacheTime' => (int) env('STRAPI_CACHE_TIME', 3600), 20 | 21 | // Token for authentication 22 | 'token' => env('STRAPI_TOKEN', null), 23 | ]; 24 | -------------------------------------------------------------------------------- /grumphp.yml.dist: -------------------------------------------------------------------------------- 1 | grumphp: 2 | process_timeout: null 3 | tasks: 4 | composer: ~ 5 | 6 | composer_normalize: ~ 7 | 8 | phpcsfixer: 9 | config: .php-cs-fixer.dist.php 10 | 11 | #https://www.php.net/supported-versions.php 12 | phpversion: 13 | project: '8.2' 14 | 15 | psalm: 16 | config: psalm.xml.dist 17 | show_info: true 18 | 19 | #FIXME - upgrade to PHP 8.x 20 | #rector: ~ 21 | 22 | securitychecker_roave: 23 | run_always: true 24 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | withPaths([ 20 | __DIR__.'/config', 21 | __DIR__.'/src', 22 | __DIR__.'/tests', 23 | ]) 24 | ->withRules([ 25 | InlineConstructorDefaultToPropertyRector::class, 26 | ]) 27 | ->withSets([ 28 | // define sets of rules 29 | LevelSetList::UP_TO_PHP_74, 30 | ]) 31 | ; 32 | -------------------------------------------------------------------------------- /src/Commands/LaravelStrapiCommand.php: -------------------------------------------------------------------------------- 1 | strapiUrl = config('strapi.url'); 34 | $this->cacheTime = config('strapi.cacheTime'); 35 | $this->token = config('strapi.token'); 36 | 37 | if (!empty($this->token)) { 38 | $this->headers['Authorization'] = 'Bearer ' . $this->token; 39 | } 40 | } 41 | 42 | public function collection(string $type, $sortKey = 'id', $sortOrder = 'DESC', $limit = 20, $start = 0, $fullUrls = true, array|string $populate = [], array $queryData = []): array 43 | { 44 | $endpoint = $this->strapiUrl . '/' . $type; 45 | 46 | $queryData['sort'][0] = $sortKey . ':' . $sortOrder; 47 | $queryData['pagination']['limit'] = $limit; 48 | $queryData['pagination']['start'] = $start; 49 | 50 | if (!empty($populate)) { 51 | $queryData['populate'] = $populate; 52 | } 53 | 54 | $endpoint .= '?' . http_build_query($queryData); 55 | 56 | $cacheKey = self::CACHE_KEY . '.' . __FUNCTION__ . '.' . encrypt($endpoint); 57 | 58 | // Fetch and cache the collection type 59 | $return = Cache::remember($cacheKey, $this->cacheTime, function () use ($endpoint) { 60 | $response = Http::withHeaders($this->headers)->get($endpoint); 61 | 62 | return $response->json(); 63 | }); 64 | 65 | if (isset($return['statusCode']) && $return['statusCode'] >= 400) { 66 | Cache::forget($cacheKey); 67 | 68 | throw new PermissionDenied('Strapi returned a ' . $return['statusCode']); 69 | } 70 | 71 | if (!is_array($return)) { 72 | Cache::forget($cacheKey); 73 | 74 | if (null === $return) { 75 | throw new NotFound('The requested single entry (' . $type . ') was null'); 76 | } 77 | 78 | throw new UnknownError('An unknown Strapi error was returned'); 79 | } 80 | 81 | // Replace any relative URLs with the full path 82 | if ($fullUrls) { 83 | $return = $this->convertToFullUrls($return); 84 | } 85 | 86 | return $return; 87 | } 88 | 89 | public function collectionCount(string $type): int 90 | { 91 | $endpoint = $this->strapiUrl . '/' . $type . '/count'; 92 | 93 | $cacheKey = self::CACHE_KEY . '.' . __FUNCTION__ . '.' . encrypt($endpoint); 94 | 95 | return Cache::remember($cacheKey, $this->cacheTime, function () use ($endpoint) { 96 | $response = Http::withHeaders($this->headers)->get($endpoint); 97 | 98 | return $response->json(); 99 | }); 100 | } 101 | 102 | public function entry(string $type, string $documentId, $fullUrls = true, array|string $populate = [], array $queryData = []): array 103 | { 104 | $endpoint = $this->strapiUrl . '/' . $type . '/' . $documentId; 105 | 106 | if (!empty($populate)) { 107 | $queryData['populate'] = $populate; 108 | } 109 | 110 | $endpoint .= '?' . http_build_query($queryData); 111 | 112 | $cacheKey = self::CACHE_KEY . '.' . __FUNCTION__ . '.' . encrypt($endpoint); 113 | 114 | $return = Cache::remember($cacheKey, $this->cacheTime, function () use ($endpoint) { 115 | $response = Http::withHeaders($this->headers)->get($endpoint); 116 | 117 | return $response->json(); 118 | }); 119 | 120 | if (isset($return['statusCode']) && $return['statusCode'] >= 400) { 121 | Cache::forget($cacheKey); 122 | 123 | throw new PermissionDenied('Strapi returned a ' . $return['statusCode']); 124 | } 125 | 126 | if (!is_array($return)) { 127 | Cache::forget($cacheKey); 128 | 129 | if (null === $return) { 130 | throw new NotFound('The requested single entry (' . $type . ') was null'); 131 | } 132 | 133 | throw new UnknownError('An unknown Strapi error was returned'); 134 | } 135 | 136 | if ($fullUrls) { 137 | $return = $this->convertToFullUrls($return); 138 | } 139 | 140 | return $return; 141 | } 142 | 143 | public function entriesByField(string $type, string $fieldName, $fieldValue, $fullUrls = true, array|string $populate = [], array $queryData = []): array 144 | { 145 | $endpoint = $this->strapiUrl . '/' . $type; 146 | 147 | $queryData['filters'][$fieldName]['$eq'] = $fieldValue; 148 | 149 | if (!empty($populate)) { 150 | $queryData['populate'] = $populate; 151 | } 152 | 153 | $endpoint .= '?' . http_build_query($queryData); 154 | 155 | $cacheKey = self::CACHE_KEY . '.' . __FUNCTION__ . '.' . encrypt($endpoint); 156 | 157 | $entries = Cache::remember($cacheKey, $this->cacheTime, function () use ($endpoint) { 158 | $response = Http::withHeaders($this->headers)->get($endpoint); 159 | 160 | return $response->json(); 161 | }); 162 | 163 | if (isset($entries['statusCode']) && $entries['statusCode'] >= 400) { 164 | Cache::forget($cacheKey); 165 | 166 | throw new PermissionDenied('Strapi returned a ' . $entries['statusCode']); 167 | } 168 | 169 | if (!is_array($entries)) { 170 | Cache::forget($cacheKey); 171 | 172 | if (null === $entries) { 173 | throw new NotFound('The requested entries by field (' . $type . ') were not found'); 174 | } 175 | 176 | throw new UnknownError('An unknown Strapi error was returned'); 177 | } 178 | 179 | if ($fullUrls) { 180 | $entries = $this->convertToFullUrls($entries); 181 | } 182 | 183 | return $entries; 184 | } 185 | 186 | public function single(string $type, string $pluck = null, $fullUrls = true, array|string $populate = [], array $queryData = []): array 187 | { 188 | $endpoint = $this->strapiUrl . '/' . $type; 189 | 190 | if (!empty($populate)) { 191 | $queryData['populate'] = $populate; 192 | } 193 | 194 | $endpoint .= '?' . http_build_query($queryData); 195 | 196 | $cacheKey = self::CACHE_KEY . '.' . __FUNCTION__ . '.' . encrypt($endpoint); 197 | 198 | // Fetch and cache the collection type 199 | $return = Cache::remember($cacheKey, $this->cacheTime, function () use ($endpoint) { 200 | $response = Http::withHeaders($this->headers)->get($endpoint); 201 | 202 | return $response->json(); 203 | }); 204 | 205 | if (isset($return['statusCode']) && $return['statusCode'] >= 400) { 206 | Cache::forget($cacheKey); 207 | 208 | throw new PermissionDenied('Strapi returned a ' . $return['statusCode']); 209 | } 210 | 211 | if (!is_array($return)) { 212 | Cache::forget($cacheKey); 213 | 214 | if (null === $return) { 215 | throw new NotFound('The requested single entry (' . $type . ') was null'); 216 | } 217 | 218 | throw new UnknownError('An unknown Strapi error was returned'); 219 | } 220 | 221 | // Replace any relative URLs with the full path 222 | if ($fullUrls) { 223 | $return = $this->convertToFullUrls($return); 224 | } 225 | 226 | if (null !== $pluck && isset($return[$pluck])) { 227 | return $return[$pluck]; 228 | } 229 | 230 | return $return; 231 | } 232 | 233 | /** 234 | * Function to create new entries in the Strapi DB. 235 | */ 236 | public function create(string $type, array $data): array 237 | { 238 | $endpoint = "$this->strapiUrl/$type"; 239 | $response = Http::withHeaders($this->headers)->post($endpoint, ['data' => $data]); 240 | 241 | return $response->json(); 242 | } 243 | 244 | /** 245 | * Function to create new entries in the Strapi DB. 246 | */ 247 | public function update(string $type, int|string $id, array $data): array 248 | { 249 | $endpoint = "$this->strapiUrl/$type/$id"; 250 | $response = Http::withHeaders($this->headers)->put($endpoint, ['data' => $data]); 251 | 252 | return $response->json(); 253 | } 254 | 255 | /** 256 | * This function adds the Strapi URL to the front of content in entries, collections, etc. 257 | * This is primarily used to change image URLs to actually point to Strapi. 258 | * 259 | * @param mixed $array 260 | */ 261 | private function convertToFullUrls($array): array 262 | { 263 | foreach ($array as $key => $item) { 264 | if (is_array($item)) { 265 | $array[$key] = $this->convertToFullUrls($item); 266 | } 267 | 268 | if (!is_string($item) || empty($item)) { 269 | continue; 270 | } 271 | 272 | $array[$key] = preg_replace('/!\[(.*)\]\((.*)\)/', '![$1](' . config('strapi.url') . '$2)', $item); 273 | } 274 | 275 | return $array; 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/LaravelStrapiFacade.php: -------------------------------------------------------------------------------- 1 | name('laravel-strapi') 26 | ->hasConfigFile() 27 | ->hasCommand(LaravelStrapiCommand::class) 28 | ; 29 | } 30 | } 31 | --------------------------------------------------------------------------------