Complete PHP EduDex API client scaffolded from openapi-edudex-spec.json, following SilverStripe 5 coding standards.
Total Files Created: 28 files across 8 directories
app/src/EduDex/
├── Client.php # Main API client entry point
├── Endpoints/ # API endpoint classes
│ ├── BaseEndpoint.php
│ ├── Organizations.php
│ ├── Suppliers.php
│ ├── Accreditors.php
│ ├── Programs.php
│ └── Validations.php
├── Models/ # Data models from OpenAPI schemas
│ ├── Model.php # Base model with hydration
│ ├── Organization.php
│ ├── Supplier.php
│ ├── Accreditor.php
│ ├── Accreditation.php
│ ├── StaticCatalog.php
│ ├── DynamicCatalog.php
│ ├── Webhook.php
│ ├── Program.php
│ └── ValidationResult.php
├── Types/ # Helper types
│ ├── LocalizedString.php
│ └── ValidationMessage.php
├── Exceptions/ # Exception hierarchy
│ ├── EduDexException.php
│ ├── AuthenticationException.php
│ ├── ValidationException.php
│ └── ApiException.php
├── Http/ # HTTP client abstraction
│ ├── ClientInterface.php
│ └── GuzzleClient.php
├── Extensions/ # SilverStripe extensions
│ └── SiteConfigExtension.php
└── README.md # Complete usage documentation
- ✅ Main
Clientclass with endpoint accessors - ✅
BaseEndpointwith common HTTP methods and model hydration - ✅ Abstract
Modelclass with fromArray/toArray serialization - ✅ HTTP client abstraction (Guzzle implementation)
- ✅ Complete exception hierarchy
- ✅ Organizations - List, get, catalogs (static/dynamic), webhooks
- ✅ Suppliers - List, get, programs, metadata, discounts
- ✅ Accreditors - List, get, accreditations (CRUD)
- ✅ Programs - Bulk operations
- ✅ Validations - Program, institute, discount validation
- ✅ Organization (with roles, accreditations, helper methods)
- ✅ Supplier, Accreditor (with localized names)
- ✅ Accreditation (with validity checks, expiry calculations)
- ✅ StaticCatalog, DynamicCatalog (with helper methods)
- ✅ Webhook (with status checking)
- ✅ Program (with title/description extraction)
- ✅ ValidationResult (with error/warning separation)
- ✅
LocalizedString- Multi-language content with fallback logic - ✅
ValidationMessage- Structured validation messages
- ✅ Config API support with YML configuration
- ✅ Environment variable support (
EDUDEX_API_TOKEN) - ✅ SiteConfig extension for admin UI
- ✅ Connection status testing in CMS
- ✅ Injector/Singleton support
- ✅ PHP 8.1+ with strict type hints
- ✅ Full PHPDoc blocks on all public methods
- ✅ SilverStripe coding standards (4 spaces, naming conventions)
- ✅ PSR-4 autoloading
- ✅ Comprehensive error handling
Add to .env:
EDUDEX_API_TOKEN="your-jwt-bearer-token"ddev exec vendor/bin/sake dev/build flush=1Via CMS:
- Go to Settings → EduDex tab
- Enter bearer token
- Check connection status
Via Code:
use Restruct\EduDex\Client;
$client = new Client();
$orgs = $client->organizations()->list();
echo "Found " . count($orgs) . " organizations\n";Create app/tests/EduDex/ with PHPUnit tests for:
- Model hydration/serialization
- LocalizedString fallback logic
- Exception handling
- Endpoint method signatures
Example test structure:
app/tests/EduDex/
├── ClientTest.php
├── Models/
│ ├── OrganizationTest.php
│ └── LocalizedStringTest.php
└── Endpoints/
└── OrganizationsTest.php
Extend endpoint classes to cache responses:
use SilverStripe\Core\Cache\CacheFactory;
protected function getCached(string $key, callable $callback, int $ttl = 3600)
{
$cache = CacheFactory::inst()->create('EduDex');
if ($data = $cache->get($key)) {
return $data;
}
$data = $callback();
$cache->set($key, $data, $ttl);
return $data;
}For syncing large datasets:
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
class SyncEduDexJob extends AbstractQueuedJob
{
public function process()
{
$client = Client::singleton();
// Sync logic here
}
}use Restruct\EduDex\Client;
$client = new Client();
// Get organizations
$orgs = $client->organizations()->list();
// Validate program before submission
$result = $client->validations()->validateProgram($programData);
if ($result->isValid()) {
$client->suppliers()->upsertProgram($supplierId, $programId, $clientId, $programData);
}class CourseController extends ContentController
{
public function index()
{
$client = Client::singleton();
try {
$suppliers = $client->suppliers()->list();
return $this->customise([
'Suppliers' => $suppliers
]);
} catch (EduDexException $e) {
return $this->httpError(500, 'Failed to load suppliers');
}
}
}class ImportCoursesTask extends BuildTask
{
public function run($request)
{
$client = Client::singleton();
$suppliers = $client->suppliers()->list();
foreach ($suppliers as $supplier) {
$programs = $client->suppliers()->listPrograms($supplier->id);
// Process programs...
}
}
}# Note: Bearer token must be set via EDUDEX_API_TOKEN environment variable
Restruct\EduDex\Integration\SilverStripe\SilverStripeClient:
api_base_url: 'https://api.edudex.nl/data/v1/'
timeout: 30
debug: false
cache_ttl: 3600
# Map base Client to SilverStripeClient via Injector
SilverStripe\Core\Injector\Injector:
Restruct\EduDex\Client:
class: Restruct\EduDex\Integration\SilverStripe\SilverStripeClientEDUDEX_API_TOKEN- JWT bearer token (required, cannot be set via Config)
- List organizations
- Get organization
- List/Get/Create/Update/Delete static catalogs
- Bulk add/remove programs to/from static catalogs
- List/Get/Create/Update/Delete dynamic catalogs
- Add/Remove suppliers from dynamic catalogs
- List/Get/Create/Update/Delete/Test webhooks
- List suppliers
- Get supplier
- Get/Update metadata
- List/Get/Upsert/Delete programs
- List/Get/Upsert/Delete discounts
- List accreditors
- Get accreditor
- List/Get/Create/Update/Delete accreditations
- Bulk retrieve programs
- Validate programs
- Validate institute metadata
- Validate discounts
- README.md - Complete usage guide with examples
- IMPLEMENTATION.md - This file (implementation details)
- openapi-edudex-spec.json - Original OpenAPI specification
Models automatically cast properties:
- DateTime strings → DateTime objects
- Localized arrays → LocalizedString objects
- Validation messages → ValidationMessage objects
- Booleans, integers, floats properly typed
LocalizedString supports:
- Multiple languages (nl, en, de, fr, etc.)
- Automatic fallback (nl → en → first available)
- Easy modification and serialization
Four exception types with specific use cases:
EduDexException- Base for all errorsAuthenticationException- 401/403 errorsValidationException- 400 with validation messagesApiException- Other HTTP errors (404, 500, etc.)
All classes follow SOLID principles:
- HTTP client is abstracted (easy to swap)
- Models use protected methods for custom casting
- Endpoints extend BaseEndpoint
- SilverStripe Injector support
To update when API changes:
- Update
openapi-edudex-spec.json - Review new/changed schemas
- Update corresponding Model classes
- Update Endpoint methods
- Update README examples
- Run tests
For issues or questions:
- API Docs: https://api.edudex.nl/data/v1/
- OpenAPI Spec:
openapi-edudex-spec.json - This README:
app/src/EduDex/README.md