ver. 0.296: REST API for ordersPRO — orders management, dictionaries, API key auth
- New API layer: ApiRouter, OrdersApiController, DictionariesApiController - Orders API: list (with filters/pagination/updated_since), details, change status, set paid/unpaid - Dictionaries API: order statuses, transport methods, payment methods - X-Api-Key authentication via pp_settings.api_key - OrderRepository: listForApi(), findForApi(), touchUpdatedAt() - updated_at column on pp_shop_orders for polling support - api.php: skip session for API requests, route to ApiRouter - SettingsController: api_key field in system tab - 30 new tests (666 total, 1930 assertions) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
82
autoload/api/Controllers/DictionariesApiController.php
Normal file
82
autoload/api/Controllers/DictionariesApiController.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
namespace api\Controllers;
|
||||
|
||||
use api\ApiRouter;
|
||||
use Domain\ShopStatus\ShopStatusRepository;
|
||||
use Domain\Transport\TransportRepository;
|
||||
use Domain\PaymentMethod\PaymentMethodRepository;
|
||||
|
||||
class DictionariesApiController
|
||||
{
|
||||
private $statusRepo;
|
||||
private $transportRepo;
|
||||
private $paymentRepo;
|
||||
|
||||
public function __construct(
|
||||
ShopStatusRepository $statusRepo,
|
||||
TransportRepository $transportRepo,
|
||||
PaymentMethodRepository $paymentRepo
|
||||
) {
|
||||
$this->statusRepo = $statusRepo;
|
||||
$this->transportRepo = $transportRepo;
|
||||
$this->paymentRepo = $paymentRepo;
|
||||
}
|
||||
|
||||
public function statuses(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('GET')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$statuses = $this->statusRepo->allStatuses();
|
||||
|
||||
$result = [];
|
||||
foreach ($statuses as $id => $name) {
|
||||
$result[] = [
|
||||
'id' => (int)$id,
|
||||
'name' => (string)$name,
|
||||
];
|
||||
}
|
||||
|
||||
ApiRouter::sendSuccess($result);
|
||||
}
|
||||
|
||||
public function transports(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('GET')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transports = $this->transportRepo->allActive();
|
||||
|
||||
$result = [];
|
||||
foreach ($transports as $transport) {
|
||||
$result[] = [
|
||||
'id' => (int)($transport['id'] ?? 0),
|
||||
'name' => (string)($transport['name_visible'] ?? $transport['name'] ?? ''),
|
||||
'cost' => (float)($transport['cost'] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
ApiRouter::sendSuccess($result);
|
||||
}
|
||||
|
||||
public function payment_methods(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('GET')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$methods = $this->paymentRepo->allActive();
|
||||
|
||||
$result = [];
|
||||
foreach ($methods as $method) {
|
||||
$result[] = [
|
||||
'id' => (int)($method['id'] ?? 0),
|
||||
'name' => (string)($method['name'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
ApiRouter::sendSuccess($result);
|
||||
}
|
||||
}
|
||||
154
autoload/api/Controllers/OrdersApiController.php
Normal file
154
autoload/api/Controllers/OrdersApiController.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
namespace api\Controllers;
|
||||
|
||||
use api\ApiRouter;
|
||||
use Domain\Order\OrderAdminService;
|
||||
use Domain\Order\OrderRepository;
|
||||
|
||||
class OrdersApiController
|
||||
{
|
||||
private $service;
|
||||
private $orderRepo;
|
||||
|
||||
public function __construct(OrderAdminService $service, OrderRepository $orderRepo)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->orderRepo = $orderRepo;
|
||||
}
|
||||
|
||||
public function list(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('GET')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filters = [
|
||||
'status' => isset($_GET['status']) ? $_GET['status'] : '',
|
||||
'paid' => isset($_GET['paid']) ? $_GET['paid'] : '',
|
||||
'date_from' => isset($_GET['date_from']) ? $_GET['date_from'] : '',
|
||||
'date_to' => isset($_GET['date_to']) ? $_GET['date_to'] : '',
|
||||
'updated_since' => isset($_GET['updated_since']) ? $_GET['updated_since'] : '',
|
||||
'number' => isset($_GET['number']) ? $_GET['number'] : '',
|
||||
'client' => isset($_GET['client']) ? $_GET['client'] : '',
|
||||
];
|
||||
|
||||
$page = max(1, (int)(isset($_GET['page']) ? $_GET['page'] : 1));
|
||||
$perPage = max(1, min(100, (int)(isset($_GET['per_page']) ? $_GET['per_page'] : 50)));
|
||||
|
||||
$result = $this->orderRepo->listForApi($filters, $page, $perPage);
|
||||
|
||||
ApiRouter::sendSuccess($result);
|
||||
}
|
||||
|
||||
public function get(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('GET')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
|
||||
if ($id <= 0) {
|
||||
ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $this->orderRepo->findForApi($id);
|
||||
if ($order === null) {
|
||||
ApiRouter::sendError('NOT_FOUND', 'Order not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
ApiRouter::sendSuccess($order);
|
||||
}
|
||||
|
||||
public function change_status(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('PUT')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
|
||||
if ($id <= 0) {
|
||||
ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$body = ApiRouter::getJsonBody();
|
||||
if ($body === null || !isset($body['status_id'])) {
|
||||
ApiRouter::sendError('BAD_REQUEST', 'Missing status_id in request body', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$statusId = (int)$body['status_id'];
|
||||
$sendEmail = !empty($body['send_email']);
|
||||
|
||||
$order = $this->orderRepo->findRawById($id);
|
||||
if ($order === null) {
|
||||
ApiRouter::sendError('NOT_FOUND', 'Order not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->service->changeStatus($id, $statusId, $sendEmail);
|
||||
|
||||
ApiRouter::sendSuccess([
|
||||
'order_id' => $id,
|
||||
'status_id' => $statusId,
|
||||
'changed' => !empty($result['result']),
|
||||
]);
|
||||
}
|
||||
|
||||
public function set_paid(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('PUT')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
|
||||
if ($id <= 0) {
|
||||
ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $this->orderRepo->findRawById($id);
|
||||
if ($order === null) {
|
||||
ApiRouter::sendError('NOT_FOUND', 'Order not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$body = ApiRouter::getJsonBody();
|
||||
$sendEmail = ($body !== null && !empty($body['send_email']));
|
||||
|
||||
$this->service->setOrderAsPaid($id, $sendEmail);
|
||||
|
||||
ApiRouter::sendSuccess([
|
||||
'order_id' => $id,
|
||||
'paid' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
public function set_unpaid(): void
|
||||
{
|
||||
if (!ApiRouter::requireMethod('PUT')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = (int)(isset($_GET['id']) ? $_GET['id'] : 0);
|
||||
if ($id <= 0) {
|
||||
ApiRouter::sendError('BAD_REQUEST', 'Missing or invalid id parameter', 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $this->orderRepo->findRawById($id);
|
||||
if ($order === null) {
|
||||
ApiRouter::sendError('NOT_FOUND', 'Order not found', 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->service->setOrderAsUnpaid($id);
|
||||
|
||||
ApiRouter::sendSuccess([
|
||||
'order_id' => $id,
|
||||
'paid' => 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user