feat: Add Transport module with repository, controller, and views
- Implemented TransportRepository for managing transport data with methods for listing, finding, saving, and retrieving transport costs. - Created ShopTransportController to handle transport-related actions, including listing, editing, and saving transports. - Added views for transport management: transports list and transport edit forms. - Introduced JavaScript for responsive tabs in transport edit view. - Updated testing suite with comprehensive unit tests for TransportRepository and ShopTransportController. - Increased test coverage with new assertions and scenarios for transport functionalities.
This commit is contained in:
342
autoload/Domain/Transport/TransportRepository.php
Normal file
342
autoload/Domain/Transport/TransportRepository.php
Normal file
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
namespace Domain\Transport;
|
||||
|
||||
class TransportRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function listForAdmin(
|
||||
array $filters = [],
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'st.id',
|
||||
'name' => 'st.name',
|
||||
'status' => 'st.status',
|
||||
'cost' => 'st.cost',
|
||||
'max_wp' => 'st.max_wp',
|
||||
'default' => 'st.default',
|
||||
'o' => 'st.o',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'st.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'st.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'st.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_transports AS st
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
st.id,
|
||||
st.name,
|
||||
st.name_visible,
|
||||
st.description,
|
||||
st.status,
|
||||
st.cost,
|
||||
st.max_wp,
|
||||
st.default,
|
||||
st.apilo_carrier_account_id,
|
||||
st.delivery_free,
|
||||
st.o
|
||||
FROM pp_shop_transports AS st
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, st.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item = $this->normalizeTransport($item);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $transportId): ?array
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transport = $this->db->get('pp_shop_transports', '*', ['id' => $transportId]);
|
||||
|
||||
if (!is_array($transport)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transport = $this->normalizeTransport($transport);
|
||||
|
||||
$paymentMethods = $this->db->select(
|
||||
'pp_shop_transport_payment_methods',
|
||||
'id_payment_method',
|
||||
['id_transport' => $transportId]
|
||||
);
|
||||
|
||||
$transport['payment_methods'] = is_array($paymentMethods) ? $paymentMethods : [];
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
public function save(array $data): ?int
|
||||
{
|
||||
$transportId = isset($data['id']) ? (int)$data['id'] : 0;
|
||||
$name = trim((string)($data['name'] ?? ''));
|
||||
$nameVisible = trim((string)($data['name_visible'] ?? ''));
|
||||
$description = trim((string)($data['description'] ?? ''));
|
||||
$status = $this->toSwitchValue($data['status'] ?? 0);
|
||||
$cost = isset($data['cost']) ? (float)$data['cost'] : 0.0;
|
||||
$maxWp = isset($data['max_wp']) && $data['max_wp'] !== '' ? (int)$data['max_wp'] : null;
|
||||
$default = $this->toSwitchValue($data['default'] ?? 0);
|
||||
$apiloCarrierAccountId = isset($data['apilo_carrier_account_id']) && $data['apilo_carrier_account_id'] !== ''
|
||||
? (int)$data['apilo_carrier_account_id']
|
||||
: null;
|
||||
$deliveryFree = $this->toSwitchValue($data['delivery_free'] ?? 0);
|
||||
$paymentMethods = $data['payment_methods'] ?? [];
|
||||
|
||||
if ($default === 1) {
|
||||
$this->db->update('pp_shop_transports', ['default' => 0]);
|
||||
}
|
||||
|
||||
$transportData = [
|
||||
'name' => $name,
|
||||
'name_visible' => $nameVisible,
|
||||
'description' => $description,
|
||||
'status' => $status,
|
||||
'default' => $default,
|
||||
'cost' => $cost,
|
||||
'max_wp' => $maxWp,
|
||||
'apilo_carrier_account_id' => $apiloCarrierAccountId,
|
||||
'delivery_free' => $deliveryFree,
|
||||
];
|
||||
|
||||
if (!$transportId) {
|
||||
$this->db->insert('pp_shop_transports', $transportData);
|
||||
$id = $this->db->id();
|
||||
|
||||
if ($id) {
|
||||
$this->savePaymentMethodLinks((int)$id, $paymentMethods);
|
||||
return (int)$id;
|
||||
}
|
||||
|
||||
return null;
|
||||
} else {
|
||||
$this->db->update('pp_shop_transports', $transportData, ['id' => $transportId]);
|
||||
$this->db->delete('pp_shop_transport_payment_methods', ['id_transport' => $transportId]);
|
||||
$this->savePaymentMethodLinks($transportId, $paymentMethods);
|
||||
return $transportId;
|
||||
}
|
||||
}
|
||||
|
||||
public function allActive(): array
|
||||
{
|
||||
$transports = $this->db->select(
|
||||
'pp_shop_transports',
|
||||
'*',
|
||||
[
|
||||
'status' => 1,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]
|
||||
);
|
||||
|
||||
if (!is_array($transports)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($transports as &$transport) {
|
||||
$transport = $this->normalizeTransport($transport);
|
||||
}
|
||||
unset($transport);
|
||||
|
||||
return $transports;
|
||||
}
|
||||
|
||||
public function findActiveById(int $transportId): ?array
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transport = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'*',
|
||||
['AND' => ['id' => $transportId, 'status' => 1]]
|
||||
);
|
||||
|
||||
if (!$transport) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->normalizeTransport($transport);
|
||||
}
|
||||
|
||||
public function getApiloCarrierAccountId(int $transportId): ?int
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'apilo_carrier_account_id',
|
||||
['id' => $transportId]
|
||||
);
|
||||
|
||||
return $result !== null ? (int)$result : null;
|
||||
}
|
||||
|
||||
public function getTransportCost(int $transportId): ?float
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'cost',
|
||||
['AND' => ['id' => $transportId, 'status' => 1]]
|
||||
);
|
||||
|
||||
return $result !== null ? (float)$result : null;
|
||||
}
|
||||
|
||||
public function lowestTransportPrice(int $wp): ?float
|
||||
{
|
||||
$result = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'cost',
|
||||
[
|
||||
'AND' => [
|
||||
'status' => 1,
|
||||
'id' => [2, 4, 6, 8, 9],
|
||||
'max_wp[>=]' => $wp
|
||||
],
|
||||
'ORDER' => ['cost' => 'ASC']
|
||||
]
|
||||
);
|
||||
|
||||
return $result !== null ? (float)$result : null;
|
||||
}
|
||||
|
||||
public function allForAdmin(): array
|
||||
{
|
||||
$transports = $this->db->select(
|
||||
'pp_shop_transports',
|
||||
'*',
|
||||
['ORDER' => ['o' => 'ASC']]
|
||||
);
|
||||
|
||||
if (!is_array($transports)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($transports as &$transport) {
|
||||
$transport = $this->normalizeTransport($transport);
|
||||
}
|
||||
unset($transport);
|
||||
|
||||
return $transports;
|
||||
}
|
||||
|
||||
private function savePaymentMethodLinks(int $transportId, $paymentMethods): void
|
||||
{
|
||||
if (is_array($paymentMethods)) {
|
||||
foreach ($paymentMethods as $paymentMethodId) {
|
||||
$this->db->insert('pp_shop_transport_payment_methods', [
|
||||
'id_payment_method' => (int)$paymentMethodId,
|
||||
'id_transport' => $transportId,
|
||||
]);
|
||||
}
|
||||
} elseif ($paymentMethods) {
|
||||
$this->db->insert('pp_shop_transport_payment_methods', [
|
||||
'id_payment_method' => (int)$paymentMethods,
|
||||
'id_transport' => $transportId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeTransport(array $transport): array
|
||||
{
|
||||
$transport['id'] = isset($transport['id']) ? (int)$transport['id'] : 0;
|
||||
$transport['status'] = $this->toSwitchValue($transport['status'] ?? 0);
|
||||
$transport['default'] = $this->toSwitchValue($transport['default'] ?? 0);
|
||||
$transport['delivery_free'] = $this->toSwitchValue($transport['delivery_free'] ?? 0);
|
||||
$transport['cost'] = isset($transport['cost']) ? (float)$transport['cost'] : 0.0;
|
||||
$transport['max_wp'] = isset($transport['max_wp']) && $transport['max_wp'] !== null
|
||||
? (int)$transport['max_wp']
|
||||
: null;
|
||||
$transport['apilo_carrier_account_id'] = isset($transport['apilo_carrier_account_id']) && $transport['apilo_carrier_account_id'] !== null
|
||||
? (int)$transport['apilo_carrier_account_id']
|
||||
: null;
|
||||
$transport['o'] = isset($transport['o']) ? (int)$transport['o'] : 0;
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user