diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml
new file mode 100644
index 0000000..3793d13
--- /dev/null
+++ b/.github/workflows/phpstan.yml
@@ -0,0 +1,29 @@
+name: PHPStan
+
+on:
+ pull_request:
+ paths:
+ - "**.php"
+ - "phpstan.neon.dist"
+ push:
+ branches:
+ - main
+
+jobs:
+ phpstan:
+ name: phpstan
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: "8.2"
+ coverage: none
+
+ - name: Install composer dependencies
+ uses: ramsey/composer-install@v3
+
+ - name: Run PHPStan
+ run: ./vendor/bin/phpstan analyse --error-format=github --debug
diff --git a/.gitignore b/.gitignore
index 841e6e5..674fc48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,4 +10,4 @@ phpunit.xml
psalm.xml
vendor
.php-cs-fixer.cache
-
+!docs
\ No newline at end of file
diff --git a/README.md b/README.md
index 1050307..393a76a 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,12 @@
-
# KvK API client (Dutch Chamber of Commerce)
[![Latest Version on Packagist](https://img.shields.io/packagist/v/vormkracht10/kvk-api.svg?style=flat-square)](https://packagist.org/packages/vormkracht10/kvk-api)
[![Tests](https://github.com/vormkracht10/kvk-api/actions/workflows/run-tests.yml/badge.svg?branch=main)](https://github.com/vormkracht10/kvk-api/actions/workflows/run-tests.yml)
[![Total Downloads](https://img.shields.io/packagist/dt/vormkracht10/kvk-api.svg?style=flat-square)](https://packagist.org/packages/vormkracht10/kvk-api)
-PHP package to communicate with the business register of the Dutch Chamber of Commerce.
+PHP package to communicate with the business register of the Dutch Chamber of Commerce.
-At the moment it is only possible to search by company name. The result will contain the following data:
+At the moment it is only possible to search by company name. The result will contain the following data:
- KvK number
@@ -25,12 +24,19 @@ You can install the package via composer:
composer require vormkracht10/kvk-api
```
+## Upgrade guide
+
+See the [upgrade guide](docs/upgrade.md) for more information on what has changed recently.
+
## Usage
+> Note: if you don't have an API key yet, get yours at the [developer portal](https://developers.kvk.nl/) of the Chamber of Commerce
+
```php
use Vormkracht10\KvkApi\ClientFactory;
-
$apiKey = '';
+
+// Optional SSL certificate
$rootCertificate = '';
$kvk = ClientFactory::create($apiKey, $rootCertificate);
@@ -38,8 +44,34 @@ $kvk = ClientFactory::create($apiKey, $rootCertificate);
// Search by company name
$companies = $kvk->search('Vormkracht10');
```
-> Note: if you don't have an API key yet, get yours at the [developer portal](https://developers.kvk.nl/) of the Chamber of Commerce
+### Search with additional parameters
+
+```php
+$companies = $kvk->search('Vormkracht10', [
+'pagina' => 1,
+'resultatenPerPagina' => 10
+]);
+```
+
+### Set page and results per page before searching
+
+```php
+$kvk->setPage(2);
+$kvk->setResultsPerPage(20);
+```
+
+### Search by KvK number
+
+```php
+$companies = $kvk->searchByKvkNumber('12345678');
+```
+
+### Search by RSIN
+
+```php
+$companies = $kvk->searchByRSIN('12345678');
+```
## Testing
@@ -53,8 +85,8 @@ Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed re
## Credits
-- [Bas van Dinther](https://github.com/Baspa)
-- [All Contributors](../../contributors)
+- [Bas van Dinther](https://github.com/Baspa)
+- [All Contributors](../../contributors)
## License
diff --git a/composer.json b/composer.json
index c601b99..b139406 100644
--- a/composer.json
+++ b/composer.json
@@ -15,13 +15,14 @@
}
],
"require": {
- "php": "^7.4|^8.0",
+ "php": "^7.4|^8.0|^8.1|^8.2|^8.3",
"guzzlehttp/guzzle": "^7.0",
- "illuminate/support": "^8.0|^9.0|^10.0"
+ "illuminate/support": "^8.0|^9.0|^10.0|^11.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"pestphp/pest": "^1.20",
+ "phpstan/phpstan": "^1.12",
"spatie/ray": "^1.28"
},
"autoload": {
diff --git a/docs/upgrade.md b/docs/upgrade.md
new file mode 100644
index 0000000..5c8ce49
--- /dev/null
+++ b/docs/upgrade.md
@@ -0,0 +1,62 @@
+# Upgrading from v1.x to v2.x
+
+## Changes in Search Results
+
+In the new version, the search results have been expanded to include more detailed information. The Company object now contains the following data:
+
+- KvK number
+- Establishment number
+- Tradename
+- Address(es) (including type, full address, street, house number, zip code, city, and country)
+- Website(s)
+
+## New Search Methods
+
+1. Search by KvK Number
+ The new version introduces the ability to search by KvK number:
+
+```php
+$companies = $kvk->searchByKvkNumber('12345678');
+```
+
+2. Search by RSIN
+ You can now search by RSIN (Rechtspersonen en Samenwerkingsverbanden Informatienummer):
+
+```php
+$companies = $kvk->searchByRSIN('12345678');
+```
+
+## Pagination Support
+
+The new version adds support for pagination in search results:
+
+```php
+$kvk->setPage(2);
+$kvk->setResultsPerPage(20);
+```
+
+Alternatively, you can pass these parameters directly to the search method:
+
+```php
+$companies = $kvk->search('Vormkracht10', [
+ 'pagina' => 1,
+ 'resultatenPerPagina' => 10
+]);
+```
+
+## Updating dependencies
+
+```bash
+{
+ "require": {
+ "vormkracht10/kvk-api": "^2.0"
+ }
+}
+```
+
+Then run `composer update` to install the new version.
+
+## Breaking changes
+
+- The structure of the Company object has changed. Make sure to update any code that relies on the specific structure of the search results.
+- The method signatures for creating the client and performing searches have been updated. Review and update your code accordingly.
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 0000000..9250404
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,7 @@
+parameters:
+ level: 8
+ paths:
+ - src
+ tmpDir: build/phpstan
+ parallel:
+ maximumNumberOfProcesses: 1
\ No newline at end of file
diff --git a/src/Client.php b/src/Client.php
index cfef1c1..46066b4 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -2,102 +2,173 @@
namespace Vormkracht10\KvKApi;
+use GuzzleHttp\ClientInterface;
use Illuminate\Support\Collection;
+use Psr\Http\Message\ResponseInterface;
+use stdClass;
use Vormkracht10\KvKApi\Company\Company;
class Client
{
- private $httpClient;
- private $baseUrl;
- private array $results;
-
- public function __construct($httpClient)
+ private ClientInterface $httpClient;
+ private string $baseUrl;
+ /** @var array */
+ private array $results = [];
+ private int $page = 1;
+ private int $resultsPerPage = 10;
+
+ public function __construct(ClientInterface $httpClient)
{
$this->httpClient = $httpClient;
- $this->baseUrl = 'https://api.kvk.nl/api/v1/';
+ $this->baseUrl = 'https://api.kvk.nl/api/v2/';
}
- public function getData(string $search)
+ /**
+ * @param array $params
+ * @return array
+ */
+ public function search(string $search, array $params = []): array
{
- $url = $this->baseUrl . 'zoeken?handelsnaam=' . $search;
+ $queryParams = array_merge([
+ 'naam' => $search,
+ 'pagina' => $this->page,
+ 'resultatenPerPagina' => $this->resultsPerPage,
+ ], $params);
+ $data = $this->getData($queryParams);
+
+ $parsedData = $this->parseData($this->decodeJson($data));
- $response = $this->httpClient->get($url);
+ foreach ($parsedData as $item) {
+ $data = $this->decodeJson($this->getRelatedData($item));
+
+ $this->results[] = new Company(
+ $data->kvkNummer ?? '',
+ $data->vestigingsnummer ?? null,
+ $data->naam ?? null,
+ $data->adres ?? null,
+ $data->websites ?? null
+ );
+ };
+
+ return $this->results;
+ }
+
+ /**
+ * @param array $params
+ */
+ private function getData(array $params): string
+ {
+ $url = $this->baseUrl . 'zoeken?' . http_build_query($params);
+
+ $response = $this->httpClient->request('GET', $url);
return $this->getJson($response);
}
- private function getJson(object $response)
+ private function getJson(ResponseInterface $response): string
{
- return $response->getBody()->getContents();
+ return (string) $response->getBody()->getContents();
}
- private function decodeJson(string $json)
+ /**
+ * @return stdClass
+ */
+ private function decodeJson(string $json): stdClass
{
- return json_decode($json);
+ return json_decode($json, false) ?: new stdClass();
}
- public function search(string $search)
+ public function setPage(int $page): self
{
- $data = $this->getData($search);
+ $this->page = $page;
- $parsedData = $this->parseData($this->decodeJson($data));
+ return $this;
+ }
- $parsedData->data->each(function ($item) {
- $data = json_decode($this->getRelatedData($item));
+ public function setResultsPerPage(int $resultsPerPage): self
+ {
+ $this->resultsPerPage = $resultsPerPage;
- $this->results[] = new Company(
- $data->kvkNummer ?? null,
- $data->vestigingsnummer ?? null,
- $data->eersteHandelsnaam ?? null,
- $data->adressen ?? null,
- $data->websites ?? null
- );
- });
+ return $this;
+ }
- return $this->results;
+ /**
+ * @param array $params
+ * @return array
+ */
+ public function searchByKvkNumber(string $kvkNumber, array $params = []): array
+ {
+ return $this->search('', array_merge(['kvkNummer' => $kvkNumber], $params));
}
- private function parseData(object $data)
+ /**
+ * @param array $params
+ * @return array
+ */
+ public function searchByRsin(string $rsin, array $params = []): array
{
- $data = collect($data->resultaten);
+ return $this->search('', array_merge(['rsin' => $rsin], $params));
+ }
- $data = $data->map(function ($value, $key) {
- $value->attributes = collect($value)->except(['type', 'links']);
+ /**
+ * @param array $params
+ * @return array
+ */
+ public function searchByVestigingsnummer(string $vestigingsnummer, array $params = []): array
+ {
+ return $this->search('', array_merge(['vestigingsnummer' => $vestigingsnummer], $params));
+ }
+ /**
+ * @return array
+ */
+ private function parseData(stdClass $data): array
+ {
+ $resultaten = $data->resultaten ?? [];
+ /** @var array $resultatenArray */
+ $resultatenArray = is_array($resultaten) ? $resultaten : [];
+
+ return array_map(function ($value) {
+ $value = (object) $value;
+ /** @var array $attributes */
+ $attributes = array_diff_key((array) $value, array_flip(['type', 'links']));
+ $value->attributes = $attributes;
$value->id = uniqid();
- $value = collect($value)->except($value->attributes->keys());
-
- $links = collect($value['links']);
-
- $links = $links->mapWithKeys(function ($value, $key) {
- return [$value->rel => $value->href];
- });
-
- $value['links'] = $links;
+ if (isset($value->links)) {
+ /** @var array $links */
+ $links = $value->links;
+ /** @var array $mappedLinks */
+ $mappedLinks = array_column($links, 'href', 'rel');
+ $value->links = $mappedLinks;
+ } else {
+ /** @var array $emptyLinks */
+ $emptyLinks = [];
+ $value->links = $emptyLinks;
+ }
+
+ $value->actief = $value->actief ?? null;
+ $value->vervallenNaam = $value->vervallenNaam ?? null;
return $value;
- });
-
- $object = new \stdClass();
-
- $object->data = $data;
-
- return $object;
+ }, $resultatenArray);
}
- private function getRelatedData($parsedData): Collection
+ private function getRelatedData(stdClass $parsedData): string
{
- $relatedData = collect();
+ $relatedData = [];
+
+ /** @var Collection $links */
+ $links = collect((array)($parsedData->links ?? []));
- collect($parsedData['links'])->each(function ($link, $key) use (&$relatedData) {
- $response = $this->httpClient->get($link);
+ $links->each(function (string $link) use (&$relatedData) {
+ $response = $this->httpClient->request('GET', $link);
$data = $this->decodeJson($this->getJson($response));
- $relatedData = $relatedData->merge($data);
+ $relatedData = array_merge($relatedData, (array) $data);
});
- return $relatedData;
+ return json_encode($relatedData) ?: '{}';
}
}
diff --git a/src/ClientFactory.php b/src/ClientFactory.php
index 41e78e7..a6fcc5c 100644
--- a/src/ClientFactory.php
+++ b/src/ClientFactory.php
@@ -7,7 +7,7 @@
class ClientFactory
{
- public static function create(string $apiKey, string $rootCertificate): KvKApiClient
+ public static function create(string $apiKey, ?string $rootCertificate = null): KvKApiClient
{
return new KvKApiClient(
self::createHttpClient($apiKey, $rootCertificate)
@@ -16,15 +16,21 @@ public static function create(string $apiKey, string $rootCertificate): KvKApiCl
private static function createHttpClient(
string $apiKey,
- string $rootCertificate
- ) {
- $client = new Client([
+ ?string $rootCertificate = null
+ ): Client {
+ if ($rootCertificate === null) {
+ return new Client([
+ 'headers' => [
+ 'apikey' => $apiKey,
+ ],
+ ]);
+ }
+
+ return new Client([
'headers' => [
'apikey' => $apiKey,
],
'verify' => $rootCertificate,
]);
-
- return $client;
}
}
diff --git a/src/Company/Address.php b/src/Company/Address.php
index 808e4d3..1b563a5 100644
--- a/src/Company/Address.php
+++ b/src/Company/Address.php
@@ -4,6 +4,13 @@
class Address
{
+ private string $type;
+ private ?string $street;
+ private ?int $houseNumber;
+ private string $postalCode;
+ private string $city;
+ private string $country;
+
public function __construct(
string $type,
?string $street,
@@ -50,6 +57,9 @@ public function getCountry(): string
return $this->country;
}
+ /**
+ * @return array
+ */
public function get(): array
{
return [
diff --git a/src/Company/Company.php b/src/Company/Company.php
index 6497958..1212b72 100644
--- a/src/Company/Company.php
+++ b/src/Company/Company.php
@@ -4,16 +4,18 @@
class Company
{
- private $kvkNumber;
-
- private $establishmentNumber;
-
- private $tradeName;
-
- private $addresses;
-
- private $websites;
+ private string $kvkNumber;
+ private ?string $establishmentNumber;
+ private ?string $tradeName;
+ /** @var array|null */
+ private ?array $addresses;
+ /** @var array|null */
+ private ?array $websites;
+ /**
+ * @param array|null $addresses
+ * @param array|null $websites
+ */
public function __construct(
string $kvkNumber,
?string $establishmentNumber,
@@ -33,7 +35,7 @@ public function getKvkNumber(): string
return $this->kvkNumber;
}
- public function getEstablishmentNumber(): string
+ public function getEstablishmentNumber(): ?string
{
return $this->establishmentNumber;
}
@@ -44,10 +46,14 @@ public function getTradeName(): ?string
}
/**
- * @return Address[]
+ * @return array|null
*/
public function getAddresses(): ?array
{
+ if ($this->addresses === null) {
+ return null;
+ }
+
$addresses = [];
foreach ($this->addresses as $address) {
@@ -64,11 +70,17 @@ public function getAddresses(): ?array
return $addresses;
}
- public function getWebsites(): array
+ /**
+ * @return array|null
+ */
+ public function getWebsites(): ?array
{
return $this->websites;
}
+ /**
+ * @return array
+ */
public function get(): array
{
return [