Install the Bundle
composer require amitrev/bash-s365-content-bundle
Ensure your composer.json meets PHP 8.2+ and Symfony 7.4 requirements.
Configure Environment Variables
Add the required .env variables (see README) and replace placeholders with your S365 API credentials.
Generate Configuration Files
Copy the template from vendor/amitrev/bash-s365-content-bundle/recipe/config/packages/s365_content.yaml to config/packages/s365_content.yaml in your project.
Register the Bundle
Ensure the bundle is enabled in config/bundles.php:
return [
// ...
Amitrev\S365ContentBundle\S365ContentBundle::class => ['all' => true],
];
First API Call Use the proxy controller to forward a request:
use Symfony\Component\HttpFoundation\Request;
$client = new \Symfony\Contracts\HttpClient\HttpClient();
$response = $client->request('GET', '/api/s365-content/proxy', [
'query' => [
'endpoint' => '/api/v1/content',
'method' => 'GET',
],
]);
Direct API Integration
Use the S365ContentClient service to interact with the API directly:
$client = $this->container->get('s365_content.client');
$response = $client->fetchContent($projectCode, $contentId);
ContentEntity) instead of raw arrays.$response = $client->fetchContent($projectCode, $contentId, ['correlation_id' => 'abc123']);
Proxy Controller Forward requests via the dedicated controller for flexibility:
# config/routes.yaml
s365_content_proxy:
path: /api/s365-content/proxy
controller: Amitrev\S365ContentBundle\Controller\ProxyController::proxy
methods: [GET, POST, PUT, DELETE]
endpoint: Target S365 API endpoint (e.g., /api/v1/content).method: HTTP method (GET, POST, etc.).data: JSON payload for POST/PUT requests.Caching Strategy Leverage built-in caching for OAuth tokens and API responses:
# config/packages/s365_content.yaml
s365_content:
cache:
enabled: true
ttl_token: 2592000 # 30 days (default)
ttl_response: 300 # 5 minutes (adjust as needed)
Domain-Driven Design (DDD) Integration
ContentEntity, ProjectEntity, etc., for business logic.
$content = new ContentEntity($responseData);
$content->publish(); // Example domain method
S365ContentClient into services for API calls.Dependency Injection Bind the client to your services via constructor injection:
use Amitrev\S365ContentBundle\Client\S365ContentClientInterface;
class MyService {
public function __construct(
private S365ContentClientInterface $s365Client
) {}
}
Event Listeners Extend functionality with events (e.g., log API calls):
use Amitrev\S365ContentBundle\Event\S365ContentEvent;
class MyListener {
public function onContentFetched(S365ContentEvent $event) {
// Custom logic (e.g., analytics, transformations)
}
}
Register in config/services.yaml:
services:
App\EventListener\MyListener:
tags:
- { name: kernel.event_listener, event: s365_content.fetched, method: onContentFetched }
Testing
Use the S365ContentClientInterface for mocking in tests:
$mockClient = $this->createMock(S365ContentClientInterface::class);
$mockClient->method('fetchContent')->willReturn(new ContentEntity([]));
$service = new MyService($mockClient);
Authentication Failures
S365_CONTENT_API_DISABLE_CACHE is false.S365_CONTENT_API_TTL_CACHED_TOKEN (e.g., to 3600 for 1 hour).S365ContentBundle events under the "Events" tab.Proxy Controller Misuse
endpoint, method) in proxy requests.use Symfony\Component\Validator\Constraints as Assert;
class ProxyRequestDto {
#[Assert\NotBlank]
public string $endpoint;
#[Assert\NotBlank]
public string $method;
}
Typed Response Mismatches
Entity structures (e.g., S365 schema changes).class CustomContentEntity extends ContentEntity {
public function __construct(array $data) {
$data['custom_field'] = $data['raw_data']['custom_field'] ?? null;
parent::__construct($data);
}
}
Correlation ID Propagation
$client->fetchContent($projectCode, $contentId, [
'correlation_id' => $this->correlationIdService->getId()
]);
Enable Verbose Logging
Add to config/packages/s365_content.yaml:
s365_content:
debug: true
Logs will appear in var/log/dev.log with detailed API request/response payloads.
HTTP Client Debugging Use Symfony’s HTTP client debug factory:
$client = $this->container->get('http_client');
$client->withOptions([
'debug' => function ($message, $level) {
error_log(sprintf('[S365] %s', $message));
},
]);
Token Cache Inspection Check cached tokens via Symfony’s cache pool:
$cache = $this->container->get('s365_content.cache.token');
$token = $cache->get('oauth_token');
Custom Authentication
Override the OAuth2 flow by implementing Amitrev\S365ContentBundle\Client\Auth\AuthenticatorInterface:
class CustomAuthenticator implements AuthenticatorInterface {
public function authenticate(): string {
// Custom logic (e.g., JWT, API key)
return 'custom_token';
}
}
Register in config/services.yaml:
services:
App\Auth\CustomAuthenticator:
tags:
- { name: s365_content.authenticator }
Response Transformers Modify how API responses are converted to entities:
use Amitrev\S365ContentBundle\Client\ResponseTransformerInterface;
class CustomTransformer implements ResponseTransformerInterface {
public function transform(array $data, string $endpoint): array {
return array_merge($data, ['processed' => true]);
}
}
Bind in config/services.yaml:
services:
App\Client\CustomTransformer:
tags:
- { name: s365_content.response_transformer }
Event Subscribers Extend the bundle’s event system for pre/post-processing:
use Amitrev\S365ContentBundle\Event\S365ContentEvents;
class MySubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents(): array {
return [
S365ContentEvents::PRE_FETCH => 'onPreFetch',
S365ContentEvents::POST_FETCH => 'onPostFetch',
];
}
How can I help you explore Laravel packages today?