|
1 | | -# Hostfact API 3.0 for Laravel |
| 1 | +# Hostfact API v3.1 for Laravel |
2 | 2 |
|
3 | | -[](https://app.fossa.io/projects/git%2Bgithub.com%2Fhyperized%2Fhostfact?ref=badge_shield) |
| 3 | +[](https://github.com/hyperized/hostfact/actions/workflows/main.yml) |
4 | 4 |
|
5 | | -Official documentation: |
6 | | ------------------------ |
| 5 | +Unofficial Laravel package for the [HostFact API v2](https://www.hostfact.nl/developer/api/). |
7 | 6 |
|
8 | | -* [Hostfact API documentation](https://www.hostfact.nl/developer/api/) |
| 7 | +## Requirements |
9 | 8 |
|
10 | | -Installation |
11 | | ------------- |
| 9 | +- PHP 8.4+ |
| 10 | +- Laravel 13 and above (auto-discovery supported) |
12 | 11 |
|
13 | | -Install using composer: |
| 12 | +## Installation |
14 | 13 |
|
15 | 14 | ```bash |
16 | 15 | composer require hyperized/hostfact |
17 | 16 | ``` |
18 | 17 |
|
19 | | -This package supports Package Auto-Discovery (Laravel 5.5+) so it doesn't require you to manually add the |
20 | | -ServiceProvider and alias. |
| 18 | +Publish the configuration: |
21 | 19 |
|
22 | | -If you are using a lower version of Laravel or not using Auto-Discovery you can add the Hostfact Service Provider to |
23 | | -the `config/app.php` file |
| 20 | +```bash |
| 21 | +php artisan vendor:publish --provider="Hyperized\Hostfact\Providers\HostfactServiceProvider" --tag="config" |
| 22 | +``` |
24 | 23 |
|
25 | | -```php |
26 | | -Hyperized\Hostfact\HostfactServiceProvider::class, |
| 24 | +## Configuration |
| 25 | + |
| 26 | +Add these to your `.env` file: |
| 27 | + |
| 28 | +```env |
| 29 | +HOSTFACT_URL=https://yoursite.tld/Pro/apiv2/api.php |
| 30 | +HOSTFACT_KEY=your-api-token |
| 31 | +HOSTFACT_TIMEOUT=20 |
27 | 32 | ``` |
28 | 33 |
|
29 | | -Register an alias for Hostfact, also in `config/app.php`: |
| 34 | +Or edit `config/Hostfact.php` directly: |
30 | 35 |
|
31 | 36 | ```php |
32 | | -'Hostfact' => Hyperized\Hostfact\HostfactServiceProvider::class, |
| 37 | +return [ |
| 38 | + 'api_v2_url' => env('HOSTFACT_URL', 'https://yoursite.tld/Pro/apiv2/api.php'), |
| 39 | + 'api_v2_key' => env('HOSTFACT_KEY', 'token'), |
| 40 | + 'api_v2_timeout' => env('HOSTFACT_TIMEOUT', 20), |
| 41 | +]; |
33 | 42 | ``` |
34 | 43 |
|
35 | | -Now publish the Hostfact package into your installation: |
| 44 | +## Usage |
36 | 45 |
|
37 | | -```bash |
38 | | -php artisan vendor:publish --provider="Hyperized\Hostfact\HostfactServiceProvider" --tag="config" |
39 | | -``` |
| 46 | +Every controller provides a static `new()` factory that reads configuration from Laravel automatically: |
| 47 | + |
| 48 | +```php |
| 49 | +use Hyperized\Hostfact\Api\Controllers\Product; |
| 50 | +use Hyperized\Hostfact\Api\Entity\Product as ProductEntity; |
| 51 | +use Hyperized\Hostfact\Api\Response\ListResponse; |
40 | 52 |
|
41 | | -This should give you a message |
42 | | -like: `Copied File [/vendor/hyperized/hostfact/config/Hostfact.php] To [/config/Hostfact.php]` |
| 53 | +$response = Product::new()->list(['searchfor' => 'hosting']); |
43 | 54 |
|
44 | | -It's possible to edit your configuration variables in the `config/Hostfact.php` file or you can use the `HOSTFACT_URL` |
45 | | -and `HOSTFACT_KEY` environment variables to store sensitive information in the `.env` file |
| 55 | +if ($response instanceof ListResponse) { |
| 56 | + echo $response->pagination->totalResults . ' results'; |
| 57 | + |
| 58 | + foreach ($response->entities as $product) { |
| 59 | + assert($product instanceof ProductEntity); |
| 60 | + echo $product->ProductName; |
| 61 | + echo $product->PriceExcl; |
| 62 | + } |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +For dependency injection or testing, use `fromHttpClient()`: |
46 | 67 |
|
47 | 68 | ```php |
48 | | -// config/Hostfact.php |
49 | | -'api_v2_url' => env('HOSTFACT_URL', 'https://yoursite.tld/Pro/apiv2/api.php'), |
50 | | -'api_v2_key' => env('HOSTFACT_KEY', 'token'), |
51 | | -'api_v2_timeout' => env('HOSTFACT_TIMEOUT', 20), |
| 69 | +use Hyperized\Hostfact\Api\Controllers\Invoice; |
52 | 70 |
|
53 | | -// .env/.env.example |
54 | | -HOSTFACT_URL=https://yoursite.tld/Pro/apiv2/api.php |
55 | | -HOSTFACT_KEY=token |
56 | | -HOSTFACT_TIMEOUT=20 |
| 71 | +$invoice = Invoice::fromHttpClient($httpClient); |
| 72 | +$result = $invoice->show(['Identifier' => 'F0001']); |
57 | 73 | ``` |
58 | 74 |
|
59 | | -Functionality |
60 | | ---------- |
| 75 | +See the [examples/](examples/) directory for more complete usage patterns. |
| 76 | + |
| 77 | +## Typed Responses |
| 78 | + |
| 79 | +All API methods return an `ApiResponse` subclass: |
| 80 | + |
| 81 | +| Response Type | When | Properties | |
| 82 | +|---|---|---| |
| 83 | +| `ShowResponse` | Single entity returned | `entity` (typed entity), `data` (DataBag) | |
| 84 | +| `ListResponse` | Multiple entities returned | `entities` (list of typed entities), `items` (list of DataBag), `pagination` | |
| 85 | +| `ActionResponse` | Action with no entity data (e.g. markAsPaid) | — | |
| 86 | +| `ErrorResponse` | API returned an error | `errors` (list of strings) | |
61 | 87 |
|
62 | | -When writing code for this Hostfact package, consider that this package has been written as a basic interface. |
| 88 | +All responses share: `controller`, `action`, `status`, `date`, `isSuccess()`, `isError()`, `toArray()`. |
63 | 89 |
|
64 | | -This package _will_ do the following: |
| 90 | +### Typed Entities |
65 | 91 |
|
66 | | -* Provide an easy way to communicate with Hostfact API controllers; |
67 | | -* Document the available API controller endpoints with methods; |
68 | | -* Transport layer (HTTP/HTTPS) error catching; |
69 | | -* Basic error parsing; |
| 92 | +Responses include typed entity objects with IDE autocompletion and strict PHP types: |
| 93 | + |
| 94 | +```php |
| 95 | +use Hyperized\Hostfact\Api\Controllers\Invoice; |
| 96 | +use Hyperized\Hostfact\Api\Entity\Invoice as InvoiceEntity; |
| 97 | +use Hyperized\Hostfact\Api\Response\ShowResponse; |
| 98 | + |
| 99 | +$response = Invoice::new()->show(['InvoiceCode' => 'F0001']); |
| 100 | +assert($response instanceof ShowResponse); |
| 101 | + |
| 102 | +$invoice = $response->entity; |
| 103 | +assert($invoice instanceof InvoiceEntity); |
| 104 | + |
| 105 | +$invoice->Identifier; // ?int |
| 106 | +$invoice->InvoiceCode; // ?string |
| 107 | +$invoice->Status; // ?InvoiceStatus (enum) |
| 108 | +$invoice->Date; // ?DateTimeImmutable |
| 109 | +$invoice->AmountExcl; // ?string (for bcmath precision) |
| 110 | +$invoice->Sent; // ?bool |
| 111 | + |
| 112 | +// Nested entities |
| 113 | +foreach ($invoice->InvoiceLines as $line) { |
| 114 | + $line->Description; // ?string |
| 115 | + $line->PriceExcl; // ?string |
| 116 | +} |
| 117 | + |
| 118 | +// Fallback to DataBag for undocumented fields |
| 119 | +$invoice->bag->string('SomeUndocumentedField'); |
| 120 | +``` |
70 | 121 |
|
71 | | -This package _will not_: |
| 122 | +For list responses: |
72 | 123 |
|
73 | | -* Parameter / input validation; |
74 | | -* Output validation; |
| 124 | +```php |
| 125 | +$response = Product::new()->list(['searchfor' => 'hosting']); |
| 126 | +assert($response instanceof ListResponse); |
| 127 | + |
| 128 | +foreach ($response->entities as $product) { |
| 129 | + assert($product instanceof ProductEntity); |
| 130 | + echo $product->ProductCode; |
| 131 | + echo $product->ProductName; |
| 132 | +} |
| 133 | +``` |
75 | 134 |
|
76 | | -You will need to consult the [Hostfact API documentation](https://www.hostfact.nl/developer/api/) to understand the |
77 | | -acceptable input and output for each of the API controllers. |
| 135 | +Available entity classes: `Product`, `Debtor`, `Invoice`, `Domain`, `Hosting`, `Ssl`, `Vps`, `Ticket`, `Order`, `PriceQuote`, `Creditor`, `Group`. Controllers without documented fields (`Service`, `CreditInvoice`, `Handle`) return a `DataBag` as the entity. |
78 | 136 |
|
79 | | -Examples |
80 | | --------- |
| 137 | +### DataBag |
81 | 138 |
|
82 | | -Example code: |
| 139 | +The raw API data is also accessible through typed methods on `DataBag`: |
83 | 140 |
|
84 | 141 | ```php |
85 | | -use \Hyperized\Hostfact\Api\Controllers\Product; |
| 142 | +$bag->string('ProductCode') // string |
| 143 | +$bag->int('Identifier') // int |
| 144 | +$bag->float('PriceExcl') // float |
| 145 | +$bag->bool('AutoRenew') // bool (handles "yes"/"no", 1/0) |
| 146 | +$bag->nullableString('Comment') // ?string |
| 147 | +$bag->nullableInt('PackageID') // ?int |
| 148 | +$bag->nullableBool('Sent') // ?bool |
| 149 | +$bag->nullableDateTime('Created') // ?DateTimeImmutable |
| 150 | +$bag->array('Groups') // array |
| 151 | +$bag->bag('Subscription') // nested DataBag |
| 152 | +$bag->bags('InvoiceLines') // list<DataBag> |
| 153 | +$bag->has('SomeField') // bool |
| 154 | +$bag['ProductCode'] // mixed (ArrayAccess) |
| 155 | +``` |
| 156 | + |
| 157 | +## Available Controllers |
| 158 | + |
| 159 | +| Controller | Actions | |
| 160 | +|---|---| |
| 161 | +| `CreditInvoice` | show, list, add, edit, delete, partialPayment, markAsPaid, lineAdd, lineDelete, attachmentAdd, attachmentDelete, attachmentDownload | |
| 162 | +| `Creditor` | show, list, add, edit, delete, attachmentAdd, attachmentDelete, attachmentDownload | |
| 163 | +| `Debtor` | show, list, add, edit, checkLogin, updateLoginCredentials, generatePdf, sendEmail, attachmentAdd, attachmentDelete, attachmentDownload | |
| 164 | +| `Domain` | show, list, add, edit, terminate, delete, getToken, lock, unlock, changeNameserver, syncWhois, editWhois, check, transfer, register, autoRenew, listDnsTemplates, getDnsZone, editDnsZone | |
| 165 | +| `Group` | show, list, add, edit, delete | |
| 166 | +| `Handle` | show, list, add, edit, delete, listDomain | |
| 167 | +| `Hosting` | show, list, add, edit, terminate, delete, suspend, unsuspend, create, removeFromServer, getDomainList, emailAccountData, upDowngrade | |
| 168 | +| `Invoice` | show, list, add, edit, delete, credit, partialPayment, markAsPaid, markAsUnpaid, sendByEmail, sendReminderByEmail, sendSummationByEmail, download, lineAdd, lineDelete, attachmentAdd, attachmentDelete, attachmentDownload, block, unblock, schedule, cancelSchedule, paymentProcessPause, paymentProcessReactivate | |
| 169 | +| `Order` | show, list, add, edit, process, lineAdd, lineDelete | |
| 170 | +| `PriceQuote` | show, list, add, edit, delete, sendByEmail, download, accept, decline, lineAdd, lineDelete, attachmentAdd, attachmentDelete, attachmentDownload | |
| 171 | +| `Product` | show, list, add, edit, delete | |
| 172 | +| `Service` | show, list, add, edit, terminate | |
| 173 | +| `Ssl` | show, list, add, edit, terminate, request, markAsInstalled, download, reissue, renew, getStatus, resendApproverEmail, revoke, markAsUninstalled | |
| 174 | +| `Ticket` | show, list, add, edit, delete, addMessage, changeStatus, changeOwner, attachmentDownload | |
| 175 | +| `Vps` | show, list, add, edit, terminate, create, start, pause, restart, suspend, unsuspend, downloadAccountData, emailAccountData | |
| 176 | + |
| 177 | +## Design |
| 178 | + |
| 179 | +This package provides a thin wrapper around the HostFact API. It does **not** validate input parameters. Consult the [HostFact API documentation](https://www.hostfact.nl/developer/api/) for accepted parameters. |
| 180 | + |
| 181 | +Architecture: |
| 182 | +- **Controllers** extend the abstract `Api` class and compose capability **traits** (e.g., `CanShow`, `CanList`, `CanAdd`) |
| 183 | +- Each controller implements a corresponding **interface** |
| 184 | +- All API methods accept `array<string, mixed>` and return typed `ApiResponse` subclasses |
| 185 | +- HTTP transport is handled by Guzzle 7.x |
| 186 | + |
| 187 | +## Testing |
| 188 | + |
| 189 | +```bash |
| 190 | +# Full test suite (PHPMD, PHPStan, PHPCS, phpmnd, PHPUnit, Infection) |
| 191 | +composer test |
| 192 | + |
| 193 | +# PHPUnit only |
| 194 | +composer phpunit -- --configuration phpunit.xml.dist |
86 | 195 |
|
87 | | -$products = Product::new() |
88 | | - ->list([ |
89 | | - 'searchfor' => 'invoice' |
90 | | - ]); |
| 196 | +# Static analysis |
| 197 | +composer phpstan -- analyse |
91 | 198 | ``` |
92 | 199 |
|
93 | 200 | ## License |
94 | 201 |
|
95 | | -[](https://app.fossa.io/projects/git%2Bgithub.com%2Fhyperized%2Fhostfact?ref=badge_large) |
| 202 | +MIT - see [LICENSE](LICENSE) for details. |
0 commit comments