answear/speedy-pickup-point-bundle
Installation
composer require answear/speedy-pickup-point-bundle
The bundle auto-registers in config/bundles.php via Symfony Flex.
Configuration
Add credentials to config/packages/answear.yaml:
answear_speedy:
username: 'your_speedy_username'
password: 'your_speedy_password'
language: 'BG' # Bulgarian (required)
clientSystemId: 12345 # Your Speedy client ID
First Use Case: Find Nearest Office
use Answear\SpeedyBundle\Command\FindOffice;
use Answear\SpeedyBundle\Request\FindOfficeRequest;
$findOffice = new FindOffice(); // Autowired via DI
$request = new FindOfficeRequest('1000', 'Sofia'); // Postcode, city
$response = $findOffice->findOffice($request);
FindOffice, GetAllSites, GetAllPostcodesFindOfficeRequest, GetAllSitesRequest, GetAllPostcodesRequestsrc/Response/ for parsed API data (e.g., OfficeResponse)// Find office by postcode/city
$request = new FindOfficeRequest('1303', 'Sofia');
$response = $findOffice->findOffice($request);
// Access office data
$office = $response->getOffice();
$schedule = $office->getOpeningSchedule(); // OpeningHours[]
// Get all Speedy sites (cached by default)
$allSites = $getAllSites->getAllSites(new GetAllSitesRequest());
// Get all supported postcodes
$postcodes = $getAllPostcodes->getAllPostcodes(new GetAllPostcodesRequest());
use Answear\SpeedyBundle\Command\FindOffice;
class PickupController extends Controller
{
public function __construct(
private FindOffice $findOffice
) {}
public function search(Request $request)
{
$requestData = new FindOfficeRequest(
$request->postcode,
$request->city
);
$office = $this->findOffice->findOffice($requestData);
return view('pickup', ['office' => $office->getOffice()]);
}
}
Create a dedicated service to encapsulate Speedy logic:
namespace App\Services;
use Answear\SpeedyBundle\Command\FindOffice;
use Answear\SpeedyBundle\Request\FindOfficeRequest;
class SpeedyService
{
public function __construct(private FindOffice $findOffice) {}
public function findNearestOffice(string $postcode, string $city)
{
$request = new FindOfficeRequest($postcode, $city);
$response = $this->findOffice->findOffice($request);
return $response->getOffice();
}
}
try-catch for Answear\SpeedyBundle\Exception\SpeedyException.answear_speedy.cache_ttl in config).answear_speedy:
debug: true
Authentication Failures
401 Unauthorized or empty responses.username/password in config and check Speedy API credentials.debug: true in config to log raw API responses.Postcode/City Validation
language: 'BG'). Non-BG postcodes/cities return null.GetAllPostcodesRequest results.Rate Limiting
Breaking Changes
guzzlehttp/guzzle:^7.0 is installed.$response = $findOffice->findOffice($request);
if ($response->isDebugEnabled()) {
\Log::debug('Raw API Response:', [$response->getRawResponse()]);
}
400: Invalid request (check postcode/city format).500: Speedy server error (contact support).429: Too many requests (add delays).Custom Requests
Extend Answear\SpeedyBundle\Request\AbstractRequest to add new endpoints:
namespace App\Speedy;
use Answear\SpeedyBundle\Request\AbstractRequest;
class CustomRequest extends AbstractRequest
{
protected string $endpoint = 'custom/endpoint';
protected array $queryParams = ['param' => 'value'];
}
Response Parsing
Override Answear\SpeedyBundle\Response\AbstractResponse to handle custom API fields:
namespace App\Speedy\Response;
use Answear\SpeedyBundle\Response\AbstractResponse;
class CustomResponse extends AbstractResponse
{
public function getCustomField(): string
{
return $this->data['custom_field'] ?? null;
}
}
Configuration Overrides
Override bundle config via config/packages/answear.yaml:
answear_speedy:
timeout: 30 # Default: 10s (from Guzzle)
cache_ttl: 3600 # Cache responses for 1 hour
GetAllSitesRequest to preload all offices for offline use (e.g., in a PickupPoint table).FindOffice in unit tests:
$this->mock(FindOffice::class)
->shouldReceive('findOffice')
->andReturn(new OfficeResponse($mockOfficeData));
How can I help you explore Laravel packages today?