ver. 0.289: ShopCategory + ShopClient frontend migration to Domain + Views + Controllers
ShopCategory: 9 frontend methods in CategoryRepository, front\Views\ShopCategory (3 methods), deleted factory + view, updated 6 callers, +17 tests. ShopClient: 13 frontend methods in ClientRepository, front\Views\ShopClient (8 methods), front\Controllers\ShopClientController (15 methods + buildEmailBody helper), deleted factory + view + controls, updated 7 callers, +36 tests. Security fix: removed hardcoded password bypass 'Legia1916'. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,26 @@ class CategoryRepository
|
||||
6 => 'alfabetycznie - Z - A',
|
||||
];
|
||||
|
||||
private const SORT_ORDER_SQL = [
|
||||
0 => 'q1.date_add ASC',
|
||||
1 => 'q1.date_add DESC',
|
||||
2 => 'q1.date_modify ASC',
|
||||
3 => 'q1.date_modify DESC',
|
||||
4 => 'q1.o ASC',
|
||||
5 => 'q1.name ASC',
|
||||
6 => 'q1.name DESC',
|
||||
];
|
||||
|
||||
private const PRODUCTS_PER_PAGE = 12;
|
||||
|
||||
private const LANGUAGE_FALLBACK_NAME_SQL = '(CASE '
|
||||
. 'WHEN copy_from IS NULL THEN name '
|
||||
. 'WHEN copy_from IS NOT NULL THEN ('
|
||||
. 'SELECT name FROM pp_shop_products_langs '
|
||||
. 'WHERE lang_id = pspl.copy_from AND product_id = psp.id'
|
||||
. ') '
|
||||
. 'END) AS name';
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
@@ -317,6 +337,330 @@ class CategoryRepository
|
||||
return (string)$title[0];
|
||||
}
|
||||
|
||||
// ===== Frontend methods =====
|
||||
|
||||
public function getCategorySort(int $categoryId): int
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "get_category_sort:$categoryId";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return (int)unserialize($objectData);
|
||||
}
|
||||
|
||||
$sortType = (int)$this->db->get('pp_shop_categories', 'sort_type', ['id' => $categoryId]);
|
||||
$cacheHandler->set($cacheKey, $sortType);
|
||||
|
||||
return $sortType;
|
||||
}
|
||||
|
||||
public function categoryName(int $categoryId, string $langId): string
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "category_name:{$langId}:{$categoryId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return (string)unserialize($objectData);
|
||||
}
|
||||
|
||||
$name = $this->db->get('pp_shop_categories_langs', 'title', [
|
||||
'AND' => [
|
||||
'category_id' => $categoryId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
$cacheHandler->set($cacheKey, $name);
|
||||
|
||||
return (string)$name;
|
||||
}
|
||||
|
||||
public function categoryUrl(int $categoryId, string $langId): string
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return '#';
|
||||
}
|
||||
|
||||
$category = $this->frontCategoryDetails($categoryId, $langId);
|
||||
if (empty($category)) {
|
||||
return '#';
|
||||
}
|
||||
|
||||
$url = !empty($category['language']['seo_link'])
|
||||
? '/' . $category['language']['seo_link']
|
||||
: '/k-' . $category['id'] . '-' . \Shared\Helpers\Helpers::seo($category['language']['title'] ?? '');
|
||||
|
||||
$currentLang = \Shared\Helpers\Helpers::get_session('current-lang');
|
||||
$defaultLang = (new \Domain\Languages\LanguagesRepository($this->db))->defaultLanguage();
|
||||
|
||||
if ($currentLang != $defaultLang && $url !== '#') {
|
||||
$url = '/' . $currentLang . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function frontCategoryDetails(int $categoryId, string $langId): array
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "front_category_details:{$categoryId}:{$langId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$category = $this->db->get('pp_shop_categories', '*', ['id' => $categoryId]);
|
||||
if (!is_array($category)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$category['language'] = $this->db->get('pp_shop_categories_langs', '*', [
|
||||
'AND' => [
|
||||
'category_id' => $categoryId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
$cacheHandler->set($cacheKey, $category);
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function categoriesTree(string $langId, ?int $parentId = null): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "categories_tree:{$langId}:{$parentId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
$results = $this->db->select('pp_shop_categories', 'id', [
|
||||
'parent_id' => $parentId,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
$category = $this->frontCategoryDetails((int)$row, $langId);
|
||||
$category['categories'] = $this->categoriesTree($langId, (int)$row);
|
||||
$categories[] = $category;
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $categories);
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function blogCategoryProducts(int $categoryId, string $langId, int $limit): array
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '' || $limit <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "blog_category_products:{$categoryId}:{$langId}:{$limit}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$rows = $this->db->query(
|
||||
'SELECT * FROM ('
|
||||
. 'SELECT '
|
||||
. 'psp.id, date_modify, date_add, o, '
|
||||
. self::LANGUAGE_FALLBACK_NAME_SQL . ' '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = :category_id AND lang_id = :lang_id'
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL '
|
||||
. 'ORDER BY '
|
||||
. 'RAND() '
|
||||
. 'LIMIT ' . (int)$limit,
|
||||
[
|
||||
':category_id' => $categoryId,
|
||||
':lang_id' => $langId,
|
||||
]
|
||||
);
|
||||
|
||||
$output = [];
|
||||
if ($rows) {
|
||||
foreach ($rows->fetchAll() as $row) {
|
||||
$output[] = $row['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function categoryProductsCount(int $categoryId, string $langId): int
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "category_products_count:{$categoryId}:{$langId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return (int)unserialize($objectData);
|
||||
}
|
||||
|
||||
$rows = $this->db->query(
|
||||
'SELECT COUNT(0) FROM ('
|
||||
. 'SELECT '
|
||||
. 'psp.id, '
|
||||
. self::LANGUAGE_FALLBACK_NAME_SQL . ' '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = :category_id AND lang_id = :lang_id'
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL',
|
||||
[
|
||||
':category_id' => $categoryId,
|
||||
':lang_id' => $langId,
|
||||
]
|
||||
);
|
||||
|
||||
$productsCount = 0;
|
||||
if ($rows) {
|
||||
$result = $rows->fetchAll();
|
||||
$productsCount = (int)($result[0][0] ?? 0);
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $productsCount);
|
||||
|
||||
return $productsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function productsId(int $categoryId, int $sortType, string $langId, int $productsLimit, int $from): array
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$order = self::SORT_ORDER_SQL[$sortType] ?? self::SORT_ORDER_SQL[0];
|
||||
$today = date('Y-m-d');
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "products_id:{$categoryId}:{$sortType}:{$langId}:{$productsLimit}:{$from}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$rows = $this->db->query(
|
||||
'SELECT * FROM ('
|
||||
. 'SELECT '
|
||||
. 'psp.id, date_modify, date_add, o, '
|
||||
. self::LANGUAGE_FALLBACK_NAME_SQL . ', '
|
||||
. '(CASE '
|
||||
. 'WHEN new_to_date >= :today THEN new_to_date '
|
||||
. 'WHEN new_to_date < :today2 THEN null '
|
||||
. 'END) AS new_to_date, '
|
||||
. '(CASE WHEN (quantity + (SELECT IFNULL(SUM(quantity),0) FROM pp_shop_products WHERE parent_id = psp.id)) > 0 THEN 1 ELSE 0 END) AS total_quantity '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = :category_id AND lang_id = :lang_id'
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL '
|
||||
. 'ORDER BY '
|
||||
. $order . ' '
|
||||
. 'LIMIT ' . (int)$from . ',' . (int)$productsLimit,
|
||||
[
|
||||
':category_id' => $categoryId,
|
||||
':lang_id' => $langId,
|
||||
':today' => $today,
|
||||
':today2' => $today,
|
||||
]
|
||||
);
|
||||
|
||||
$output = [];
|
||||
if ($rows) {
|
||||
foreach ($rows->fetchAll() as $row) {
|
||||
$output[] = $row['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{products: array, ls: int}
|
||||
*/
|
||||
public function paginatedCategoryProducts(int $categoryId, int $sortType, string $langId, int $page): array
|
||||
{
|
||||
$count = $this->categoryProductsCount($categoryId, $langId);
|
||||
if ($count <= 0) {
|
||||
return ['products' => [], 'ls' => 0];
|
||||
}
|
||||
|
||||
$totalPages = (int)ceil($count / self::PRODUCTS_PER_PAGE);
|
||||
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
} elseif ($page > $totalPages) {
|
||||
$page = $totalPages;
|
||||
}
|
||||
|
||||
$from = self::PRODUCTS_PER_PAGE * ($page - 1);
|
||||
|
||||
return [
|
||||
'products' => $this->productsId($categoryId, $sortType, $langId, self::PRODUCTS_PER_PAGE, $from),
|
||||
'ls' => $totalPages,
|
||||
];
|
||||
}
|
||||
|
||||
private function maxOrder(): int
|
||||
{
|
||||
return (int)$this->db->max('pp_shop_categories', 'o');
|
||||
|
||||
@@ -234,6 +234,287 @@ class ClientRepository
|
||||
];
|
||||
}
|
||||
|
||||
// ===== Frontend methods =====
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function clientDetails(int $clientId): ?array
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->db->get('pp_shop_clients', '*', ['id' => $clientId]) ?: null;
|
||||
}
|
||||
|
||||
public function clientEmail(int $clientId): ?string
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$email = $this->db->get('pp_shop_clients', 'email', ['id' => $clientId]);
|
||||
|
||||
return $email ? (string)$email : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function clientAddresses(int $clientId): array
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_shop_clients_addresses', '*', ['client_id' => $clientId]);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function addressDetails(int $addressId): ?array
|
||||
{
|
||||
if ($addressId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->db->get('pp_shop_clients_addresses', '*', ['id' => $addressId]) ?: null;
|
||||
}
|
||||
|
||||
public function addressDelete(int $addressId): bool
|
||||
{
|
||||
if ($addressId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_shop_clients_addresses', ['id' => $addressId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $data Keys: name, surname, street, postal_code, city, phone
|
||||
*/
|
||||
public function addressSave(int $clientId, ?int $addressId, array $data): bool
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = [
|
||||
'name' => (string)($data['name'] ?? ''),
|
||||
'surname' => (string)($data['surname'] ?? ''),
|
||||
'street' => (string)($data['street'] ?? ''),
|
||||
'postal_code' => (string)($data['postal_code'] ?? ''),
|
||||
'city' => (string)($data['city'] ?? ''),
|
||||
'phone' => (string)($data['phone'] ?? ''),
|
||||
];
|
||||
|
||||
if (!$addressId || $addressId <= 0) {
|
||||
$row['client_id'] = $clientId;
|
||||
return (bool)$this->db->insert('pp_shop_clients_addresses', $row);
|
||||
}
|
||||
|
||||
return (bool)$this->db->update('pp_shop_clients_addresses', $row, [
|
||||
'AND' => [
|
||||
'client_id' => $clientId,
|
||||
'id' => $addressId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function markAddressAsCurrent(int $clientId, int $addressId): bool
|
||||
{
|
||||
if ($clientId <= 0 || $addressId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_clients_addresses', ['current' => 0], ['client_id' => $clientId]);
|
||||
$this->db->update('pp_shop_clients_addresses', ['current' => 1], [
|
||||
'AND' => [
|
||||
'client_id' => $clientId,
|
||||
'id' => $addressId,
|
||||
],
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function clientOrders(int $clientId): array
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_shop_orders', 'id', [
|
||||
'client_id' => $clientId,
|
||||
'ORDER' => ['date_order' => 'DESC'],
|
||||
]);
|
||||
|
||||
$orders = [];
|
||||
if (is_array($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
$orders[] = \front\factory\ShopOrder::order_details($row);
|
||||
}
|
||||
}
|
||||
|
||||
return $orders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{status: string, client?: array, hash?: string, code?: string}
|
||||
*/
|
||||
public function authenticate(string $email, string $password): array
|
||||
{
|
||||
$email = trim($email);
|
||||
$password = trim($password);
|
||||
|
||||
if ($email === '' || $password === '') {
|
||||
return ['status' => 'error', 'code' => 'logowanie-nieudane'];
|
||||
}
|
||||
|
||||
$client = $this->db->get('pp_shop_clients', [
|
||||
'id', 'password', 'register_date', 'hash', 'status',
|
||||
], ['email' => $email]);
|
||||
|
||||
if (!$client) {
|
||||
return ['status' => 'error', 'code' => 'logowanie-nieudane'];
|
||||
}
|
||||
|
||||
if (!(int)$client['status']) {
|
||||
return ['status' => 'inactive', 'hash' => $client['hash']];
|
||||
}
|
||||
|
||||
if ($client['password'] !== md5($client['register_date'] . $password)) {
|
||||
return ['status' => 'error', 'code' => 'logowanie-blad-nieprawidlowe-haslo'];
|
||||
}
|
||||
|
||||
$fullClient = $this->clientDetails((int)$client['id']);
|
||||
|
||||
return ['status' => 'ok', 'client' => $fullClient];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{id: int, hash: string}|null Null when email already taken
|
||||
*/
|
||||
public function createClient(string $email, string $password, bool $agreementMarketing): ?array
|
||||
{
|
||||
$email = trim($email);
|
||||
if ($email === '' || $password === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->db->count('pp_shop_clients', ['email' => $email])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hash = md5(time() . $email);
|
||||
$registerDate = date('Y-m-d H:i:s');
|
||||
|
||||
$inserted = $this->db->insert('pp_shop_clients', [
|
||||
'email' => $email,
|
||||
'password' => md5($registerDate . $password),
|
||||
'hash' => $hash,
|
||||
'agremment_marketing' => $agreementMarketing ? 1 : 0,
|
||||
'register_date' => $registerDate,
|
||||
]);
|
||||
|
||||
if (!$inserted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int)$this->db->id(),
|
||||
'hash' => $hash,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms registration. Returns client email on success, null on failure.
|
||||
*/
|
||||
public function confirmRegistration(string $hash): ?string
|
||||
{
|
||||
$hash = trim($hash);
|
||||
if ($hash === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$id = $this->db->get('pp_shop_clients', 'id', [
|
||||
'AND' => ['hash' => $hash, 'status' => 0],
|
||||
]);
|
||||
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_clients', ['status' => 1], ['id' => $id]);
|
||||
|
||||
$email = $this->db->get('pp_shop_clients', 'email', ['id' => $id]);
|
||||
|
||||
return $email ? (string)$email : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates new password. Returns [email, password] on success, null on failure.
|
||||
*
|
||||
* @return array{email: string, password: string}|null
|
||||
*/
|
||||
public function generateNewPassword(string $hash): ?array
|
||||
{
|
||||
$hash = trim($hash);
|
||||
if ($hash === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->db->get('pp_shop_clients', ['id', 'email', 'register_date'], [
|
||||
'AND' => ['hash' => $hash, 'status' => 1, 'password_recovery' => 1],
|
||||
]);
|
||||
|
||||
if (!$data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$newPassword = substr(md5(time()), 0, 10);
|
||||
|
||||
$this->db->update('pp_shop_clients', [
|
||||
'password_recovery' => 0,
|
||||
'password' => md5($data['register_date'] . $newPassword),
|
||||
], ['id' => $data['id']]);
|
||||
|
||||
return [
|
||||
'email' => (string)$data['email'],
|
||||
'password' => $newPassword,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates password recovery. Returns hash on success, null on failure.
|
||||
*/
|
||||
public function initiatePasswordRecovery(string $email): ?string
|
||||
{
|
||||
$email = trim($email);
|
||||
if ($email === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hash = $this->db->get('pp_shop_clients', 'hash', [
|
||||
'AND' => ['email' => $email, 'status' => 1],
|
||||
]);
|
||||
|
||||
if (!$hash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_clients', ['password_recovery' => 1], ['email' => $email]);
|
||||
|
||||
return (string)$hash;
|
||||
}
|
||||
|
||||
private function normalizeTextFilter($value): string
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
|
||||
@@ -266,7 +266,7 @@ class ShopBasketController
|
||||
'basket' => \Shared\Helpers\Helpers::get_session( 'basket' ),
|
||||
'transport' => \front\factory\ShopTransport::transport( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) ),
|
||||
'payment_method' => \front\factory\ShopPaymentMethod::payment_method( \Shared\Helpers\Helpers::get_session( 'basket-payment-method-id' ) ),
|
||||
'addresses' => \front\factory\ShopClient::client_addresses( $client[ 'id' ] ),
|
||||
'addresses' => ( new \Domain\Client\ClientRepository( $GLOBALS['mdb'] ) )->clientAddresses( (int)$client['id'] ),
|
||||
'settings' => $settings,
|
||||
'coupon' => \Shared\Helpers\Helpers::get_session( 'coupon' ),
|
||||
'basket_message' => \Shared\Helpers\Helpers::get_session( 'basket_message' )
|
||||
|
||||
354
autoload/front/Controllers/ShopClientController.php
Normal file
354
autoload/front/Controllers/ShopClientController.php
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
namespace front\Controllers;
|
||||
|
||||
use Domain\Client\ClientRepository;
|
||||
|
||||
class ShopClientController
|
||||
{
|
||||
private $clientRepo;
|
||||
|
||||
public function __construct(ClientRepository $clientRepo)
|
||||
{
|
||||
$this->clientRepo = $clientRepo;
|
||||
}
|
||||
|
||||
public function markAddressAsCurrent()
|
||||
{
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if (!$client) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->clientRepo->markAddressAsCurrent(
|
||||
(int)$client['id'],
|
||||
(int)\Shared\Helpers\Helpers::get('address_id')
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function addressDelete()
|
||||
{
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if (!$client) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
$address = $this->clientRepo->addressDetails((int)\Shared\Helpers\Helpers::get('id'));
|
||||
if (!$address || $address['client_id'] != $client['id']) {
|
||||
header('Location: /panel-klienta/adresy');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($this->clientRepo->addressDelete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert(\Shared\Helpers\Helpers::lang('adres-usuniety-komunikat'));
|
||||
} else {
|
||||
\Shared\Helpers\Helpers::error(\Shared\Helpers\Helpers::lang('adres-usuniety-blad'));
|
||||
}
|
||||
|
||||
header('Location: /panel-klienta/adresy');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function addressEdit()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang('meta-title-edycja-adresu') . ' | ' . $settings['firm_name'];
|
||||
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if (!$client) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
$addressId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$address = $this->clientRepo->addressDetails($addressId);
|
||||
if ($address && $address['client_id'] != $client['id']) {
|
||||
$address = null;
|
||||
}
|
||||
|
||||
return \front\Views\ShopClient::addressEdit([
|
||||
'address' => $address,
|
||||
]);
|
||||
}
|
||||
|
||||
public function addressSave()
|
||||
{
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if (!$client) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
$addressId = (int)\Shared\Helpers\Helpers::get('address_id');
|
||||
$data = [
|
||||
'name' => \Shared\Helpers\Helpers::get('name', true),
|
||||
'surname' => \Shared\Helpers\Helpers::get('surname', true),
|
||||
'street' => \Shared\Helpers\Helpers::get('street'),
|
||||
'postal_code' => \Shared\Helpers\Helpers::get('postal_code', true),
|
||||
'city' => \Shared\Helpers\Helpers::get('city', true),
|
||||
'phone' => \Shared\Helpers\Helpers::get('phone', true),
|
||||
];
|
||||
|
||||
if ($this->clientRepo->addressSave((int)$client['id'], $addressId ?: null, $data)) {
|
||||
$msg = $addressId
|
||||
? \Shared\Helpers\Helpers::lang('zmiana-adresu-sukces')
|
||||
: \Shared\Helpers\Helpers::lang('dodawanie-nowego-adresu-sukces');
|
||||
\Shared\Helpers\Helpers::alert($msg);
|
||||
} else {
|
||||
$msg = $addressId
|
||||
? \Shared\Helpers\Helpers::lang('zmiana-adresu-blad')
|
||||
: \Shared\Helpers\Helpers::lang('dodawanie-nowego-adresu-blad');
|
||||
\Shared\Helpers\Helpers::error($msg);
|
||||
}
|
||||
|
||||
header('Location: /panel-klienta/adresy');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function clientAddresses()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang('meta-title-lista-adresow') . ' | ' . $settings['firm_name'];
|
||||
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if (!$client) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\Views\ShopClient::clientAddresses([
|
||||
'client' => $client,
|
||||
'addresses' => $this->clientRepo->clientAddresses((int)$client['id']),
|
||||
]);
|
||||
}
|
||||
|
||||
public function clientOrders()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang('meta-title-historia-zamowien') . ' | ' . $settings['firm_name'];
|
||||
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if (!$client) {
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\Views\ShopClient::clientOrders([
|
||||
'client' => $client,
|
||||
'orders' => $this->clientRepo->clientOrders((int)$client['id']),
|
||||
'statuses' => \shop\Order::order_statuses(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function newPassword()
|
||||
{
|
||||
$result = $this->clientRepo->generateNewPassword(
|
||||
(string)\Shared\Helpers\Helpers::get('hash')
|
||||
);
|
||||
|
||||
if ($result) {
|
||||
$text = $this->buildEmailBody('#nowe-haslo', [
|
||||
'[HASLO]' => $result['password'],
|
||||
]);
|
||||
\Shared\Helpers\Helpers::send_email(
|
||||
$result['email'],
|
||||
\Shared\Helpers\Helpers::lang('nowe-haslo-w-sklepie'),
|
||||
$text
|
||||
);
|
||||
\Shared\Helpers\Helpers::alert(\Shared\Helpers\Helpers::lang('nowe-haslo-zostalo-wyslane-na-twoj-adres-email'));
|
||||
}
|
||||
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function sendEmailPasswordRecovery()
|
||||
{
|
||||
$hash = $this->clientRepo->initiatePasswordRecovery(
|
||||
(string)\Shared\Helpers\Helpers::get('email')
|
||||
);
|
||||
|
||||
if ($hash) {
|
||||
$text = $this->buildEmailBody('#odzyskiwanie-hasla-link', [
|
||||
'[LINK]' => '/shopClient/new_password/hash=' . $hash,
|
||||
]);
|
||||
\Shared\Helpers\Helpers::send_email(
|
||||
(string)\Shared\Helpers\Helpers::get('email'),
|
||||
\Shared\Helpers\Helpers::lang('generowanie-nowego-hasla-w-sklepie'),
|
||||
$text
|
||||
);
|
||||
\Shared\Helpers\Helpers::alert(\Shared\Helpers\Helpers::lang('odzyskiwanie-hasla-link-komunikat'));
|
||||
} else {
|
||||
\Shared\Helpers\Helpers::alert(\Shared\Helpers\Helpers::lang('odzyskiwanie-hasla-blad'));
|
||||
}
|
||||
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function recoverPassword()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang('meta-title-odzyskiwanie-hasla') . ' | ' . $settings['firm_name'];
|
||||
|
||||
return \front\Views\ShopClient::recoverPassword();
|
||||
}
|
||||
|
||||
public function logout()
|
||||
{
|
||||
\Shared\Helpers\Helpers::delete_session('client');
|
||||
header('Location: /');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function login()
|
||||
{
|
||||
$result = $this->clientRepo->authenticate(
|
||||
(string)\Shared\Helpers\Helpers::get('email'),
|
||||
(string)\Shared\Helpers\Helpers::get('password')
|
||||
);
|
||||
|
||||
if ($result['status'] === 'inactive') {
|
||||
$link = '<a href="/ponowna-aktywacja/' . $result['hash'] . '/">'
|
||||
. ucfirst(\Shared\Helpers\Helpers::lang('wyslij-link-ponownie')) . '</a>';
|
||||
\Shared\Helpers\Helpers::alert(
|
||||
str_replace('[LINK]', $link, \Shared\Helpers\Helpers::lang('logowanie-blad-nieaktywne-konto'))
|
||||
);
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($result['status'] !== 'ok') {
|
||||
\Shared\Helpers\Helpers::alert(\Shared\Helpers\Helpers::lang($result['code']));
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::set_session('client', $result['client']);
|
||||
\Shared\Helpers\Helpers::alert(\Shared\Helpers\Helpers::lang('logowanie-udane'));
|
||||
|
||||
$redirect = \Shared\Helpers\Helpers::get('redirect');
|
||||
header('Location: ' . ($redirect ? $redirect : '/panel-klienta'));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function confirm()
|
||||
{
|
||||
$email = $this->clientRepo->confirmRegistration(
|
||||
(string)\Shared\Helpers\Helpers::get('hash')
|
||||
);
|
||||
|
||||
if ($email) {
|
||||
$text = $this->buildEmailBody('#potwierdzenie-aktywacji-konta');
|
||||
\Shared\Helpers\Helpers::send_email(
|
||||
$email,
|
||||
\Shared\Helpers\Helpers::lang('potwierdzenie-aktywacji-konta-w-sklepie') . ' ' . \Shared\Helpers\Helpers::lang('#nazwa-serwisu'),
|
||||
$text
|
||||
);
|
||||
\Shared\Helpers\Helpers::alert(\Shared\Helpers\Helpers::lang('rejestracja-potwierdzenie'));
|
||||
}
|
||||
|
||||
header('Location: /logowanie');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function signup()
|
||||
{
|
||||
$email = (string)\Shared\Helpers\Helpers::get('email');
|
||||
$password = (string)\Shared\Helpers\Helpers::get('password');
|
||||
|
||||
$created = $this->clientRepo->createClient(
|
||||
$email,
|
||||
$password,
|
||||
(bool)\Shared\Helpers\Helpers::get('agremment_marketing')
|
||||
);
|
||||
|
||||
if (!$created) {
|
||||
echo json_encode([
|
||||
'status' => 'bad',
|
||||
'msg' => \Shared\Helpers\Helpers::lang('rejestracja-email-zajety'),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$text = $this->buildEmailBody('#potwierdzenie-rejestracji', [
|
||||
'[LINK]' => '/shopClient/confirm/hash=' . $created['hash'],
|
||||
]);
|
||||
\Shared\Helpers\Helpers::send_email(
|
||||
$email,
|
||||
\Shared\Helpers\Helpers::lang('potwierdzenie-rejestracji-konta-w-sklepie') . ' ' . \Shared\Helpers\Helpers::lang('#nazwa-serwisu'),
|
||||
$text
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'ok',
|
||||
'msg' => \Shared\Helpers\Helpers::lang('rejestracja-udana'),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function loginForm()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang('meta-title-logowanie') . ' | ' . $settings['firm_name'];
|
||||
$page['class'] = 'page-login-form';
|
||||
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if ($client) {
|
||||
header('Location: /panel-klienta/zamowienia');
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\Views\ShopClient::loginForm();
|
||||
}
|
||||
|
||||
public function registerForm()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang('meta-title-rejestracja') . ' | ' . $settings['firm_name'];
|
||||
|
||||
$client = \Shared\Helpers\Helpers::get_session('client');
|
||||
if ($client) {
|
||||
header('Location: /panel-klienta/zamowienia');
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\Views\ShopClient::registerForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds email body from newsletter template with URL absolutization.
|
||||
*
|
||||
* @param array<string, string> $replacements Placeholders to replace in the template
|
||||
*/
|
||||
private function buildEmailBody(string $templateName, array $replacements = []): string
|
||||
{
|
||||
$settings = $GLOBALS['settings'];
|
||||
|
||||
$text = $settings['newsletter_header'];
|
||||
$text .= (new \Domain\Newsletter\NewsletterRepository($GLOBALS['mdb']))->templateByName($templateName);
|
||||
$text .= $settings['newsletter_footer'];
|
||||
|
||||
$base = !empty($settings['ssl']) ? 'https' : 'http';
|
||||
$serverName = $_SERVER['SERVER_NAME'] ?? '';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace($regex, '$1' . $base . '://' . $serverName . '$2$4', $text);
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace($regex, '$1' . $base . '://' . $serverName . '$2$4', $text);
|
||||
|
||||
foreach ($replacements as $placeholder => $value) {
|
||||
$text = str_replace($placeholder, $value, $text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
66
autoload/front/Views/ShopCategory.php
Normal file
66
autoload/front/Views/ShopCategory.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace front\Views;
|
||||
|
||||
class ShopCategory
|
||||
{
|
||||
public static function categoryDescription($category): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view('shop-category/category-description', [
|
||||
'category' => $category,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function categoryView($category, string $langId, int $currentPage = 1): string
|
||||
{
|
||||
global $settings, $page;
|
||||
|
||||
$categoryRepo = new \Domain\Category\CategoryRepository($GLOBALS['mdb']);
|
||||
|
||||
if (!$settings['infinitescroll']) {
|
||||
$results = $categoryRepo->paginatedCategoryProducts(
|
||||
(int)$category['id'],
|
||||
(int)($category['sort_type'] ?? 0),
|
||||
$langId,
|
||||
$currentPage
|
||||
);
|
||||
|
||||
$pager = '';
|
||||
if ($results['ls'] > 1) {
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl->ls = $results['ls'];
|
||||
$tpl->bs = $currentPage > 0 ? $currentPage : 1;
|
||||
$tpl->page = $page;
|
||||
$tpl->link = !empty($category['language']['seo_link'])
|
||||
? $category['language']['seo_link']
|
||||
: 'k-' . $category['id'] . '-' . \Shared\Helpers\Helpers::seo($category['language']['title'] ?? '');
|
||||
$pager = $tpl->render('site/pager');
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-category/category', [
|
||||
'category' => $category,
|
||||
'products' => $results['products'],
|
||||
'pager' => $pager,
|
||||
'category_description' => $currentPage <= 1,
|
||||
'category_additional_text' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$productsCount = $categoryRepo->categoryProductsCount((int)$category['id'], $langId);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-category/category-infinitescroll', [
|
||||
'category' => $category,
|
||||
'products_count' => $productsCount,
|
||||
'category_description' => $currentPage <= 1,
|
||||
'category_additional_text' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function categories($categories, $currentCategory = 0, $level = 0): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl->level = $level;
|
||||
$tpl->current_category = $currentCategory;
|
||||
$tpl->categories = $categories;
|
||||
return $tpl->render('shop-category/categories');
|
||||
}
|
||||
}
|
||||
80
autoload/front/Views/ShopClient.php
Normal file
80
autoload/front/Views/ShopClient.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace front\Views;
|
||||
|
||||
class ShopClient
|
||||
{
|
||||
public static function addressEdit($values): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $key => $val) {
|
||||
$tpl->$key = $val;
|
||||
}
|
||||
}
|
||||
return $tpl->render('shop-client/address-edit');
|
||||
}
|
||||
|
||||
public static function clientAddresses($values): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $key => $val) {
|
||||
$tpl->$key = $val;
|
||||
}
|
||||
}
|
||||
return $tpl->render('shop-client/client-addresses');
|
||||
}
|
||||
|
||||
public static function clientMenu($values): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $key => $val) {
|
||||
$tpl->$key = $val;
|
||||
}
|
||||
}
|
||||
return $tpl->render('shop-client/client-menu');
|
||||
}
|
||||
|
||||
public static function clientOrders($values): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $key => $val) {
|
||||
$tpl->$key = $val;
|
||||
}
|
||||
}
|
||||
return $tpl->render('shop-client/client-orders');
|
||||
}
|
||||
|
||||
public static function recoverPassword(): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
return $tpl->render('shop-client/recover-password');
|
||||
}
|
||||
|
||||
public static function miniLogin(): string
|
||||
{
|
||||
global $client;
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl->client = $client;
|
||||
return $tpl->render('shop-client/mini-login');
|
||||
}
|
||||
|
||||
public static function loginForm($values = ''): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $key => $val) {
|
||||
$tpl->$key = $val;
|
||||
}
|
||||
}
|
||||
return $tpl->render('shop-client/login-form');
|
||||
}
|
||||
|
||||
public static function registerForm(): string
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
return $tpl->render('shop-client/register-form');
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
<?php
|
||||
namespace front\controls;
|
||||
class ShopClient
|
||||
{
|
||||
public static function mark_address_as_current()
|
||||
{
|
||||
if ( !$client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
return false;
|
||||
|
||||
\front\factory\ShopClient::mark_address_as_current( $client['id'], \Shared\Helpers\Helpers::get( 'address_id' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function address_delete()
|
||||
{
|
||||
if ( !$client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
{
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$address = \front\factory\ShopClient::address_details( \Shared\Helpers\Helpers::get( 'id' ) );
|
||||
if ( $address['client_id'] != $client['id'] )
|
||||
{
|
||||
header( 'Location: /panel-klienta/adresy' );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( \front\factory\ShopClient::address_delete( \Shared\Helpers\Helpers::get( 'id' ) ) )
|
||||
\Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'adres-usuniety-komunikat' ) );
|
||||
else
|
||||
\Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'adres-usuniety-blad' ) );
|
||||
header( 'Location: /panel-klienta/adresy' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function address_edit()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang( 'meta-title-edycja-adresu' ) . ' | ' . $settings['firm_name'];
|
||||
|
||||
if ( !$client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
{
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$address = \front\factory\ShopClient::address_details( \Shared\Helpers\Helpers::get( 'id' ) );
|
||||
if ( $address['client_id'] != $client['id'] )
|
||||
unset( $address );
|
||||
|
||||
return \front\view\ShopClient::address_edit( [
|
||||
'address' => \front\factory\ShopClient::address_details( \Shared\Helpers\Helpers::get( 'id' ) )
|
||||
] );
|
||||
}
|
||||
|
||||
public static function address_save()
|
||||
{
|
||||
if ( !$client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
{
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( \front\factory\ShopClient::address_save( $client['id'], \Shared\Helpers\Helpers::get( 'address_id' ), \Shared\Helpers\Helpers::get( 'name', true ), \Shared\Helpers\Helpers::get( 'surname', true ), \Shared\Helpers\Helpers::get( 'street' ), \Shared\Helpers\Helpers::get( 'postal_code', true ), \Shared\Helpers\Helpers::get( 'city', true ), \Shared\Helpers\Helpers::get( 'phone', true ) ) )
|
||||
{
|
||||
\Shared\Helpers\Helpers::get( 'address_id' ) ? \Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'zmiana-adresu-sukces' ) ) : \Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'dodawanie-nowego-adresu-sukces' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\Shared\Helpers\Helpers::get( 'address_id' ) ? \Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zmiana-adresu-blad' ) ) : \Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'dodawanie-nowego-adresu-blad' ) );
|
||||
}
|
||||
|
||||
header( 'Location: /panel-klienta/adresy' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function client_addresses()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang( 'meta-title-lista-adresow' ) . ' | ' . $settings['firm_name'];
|
||||
|
||||
if ( !$client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
{
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\view\ShopClient::client_addresses( [
|
||||
'client' => $client,
|
||||
'addresses' => \front\factory\ShopClient::client_addresses( $client['id'] )
|
||||
] );
|
||||
}
|
||||
|
||||
public static function client_orders()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang( 'meta-title-historia-zamowien' ) . ' | ' . $settings['firm_name'];
|
||||
|
||||
if ( !$client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
{
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\view\ShopClient::client_orders( [
|
||||
'client' => $client,
|
||||
'orders' => \front\factory\ShopClient::client_orders( $client['id'] ),
|
||||
'statuses' => \shop\Order::order_statuses()
|
||||
] );
|
||||
}
|
||||
|
||||
public static function new_password()
|
||||
{
|
||||
if ( \front\factory\ShopClient::new_password( \Shared\Helpers\Helpers::get( 'hash' ) ) )
|
||||
\Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'nowe-haslo-zostalo-wyslane-na-twoj-adres-email' ) );
|
||||
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function send_email_password_recovery()
|
||||
{
|
||||
if ( \front\factory\ShopClient::send_email_password_recovery( \Shared\Helpers\Helpers::get( 'email' ) ) )
|
||||
\Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'odzyskiwanie-hasla-link-komunikat' ) );
|
||||
else
|
||||
\Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'odzyskiwanie-hasla-blad' ) );
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function recover_password()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang( 'meta-title-odzyskiwanie-hasla' ) . ' | ' . $settings['firm_name'];
|
||||
|
||||
return \front\view\ShopClient::recover_password();
|
||||
}
|
||||
|
||||
public static function logout()
|
||||
{
|
||||
\Shared\Helpers\Helpers::delete_session( 'client' );
|
||||
header( 'Location: /' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function login()
|
||||
{
|
||||
if ( !\front\factory\ShopClient::login( \Shared\Helpers\Helpers::get( 'email' ), \Shared\Helpers\Helpers::get( 'password' ) ) )
|
||||
header( 'Location: /logowanie' );
|
||||
else
|
||||
{
|
||||
$client = \Shared\Helpers\Helpers::get_session( 'client' );
|
||||
if ( $redirect = \Shared\Helpers\Helpers::get( 'redirect' ) )
|
||||
header( 'Location: ' . $redirect );
|
||||
else
|
||||
header( 'Location: /panel-klienta' );
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function confirm()
|
||||
{
|
||||
if ( \front\factory\ShopClient::register_confirm( \Shared\Helpers\Helpers::get( 'hash' ) ) )
|
||||
\Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'rejestracja-potwierdzenie' ) );
|
||||
|
||||
header( 'Location: /logowanie' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function signup()
|
||||
{
|
||||
$result = \front\factory\ShopClient::signup( \Shared\Helpers\Helpers::get( 'email' ), \Shared\Helpers\Helpers::get( 'password' ), \Shared\Helpers\Helpers::get( 'agremment_marketing' ) );
|
||||
echo json_encode( $result );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function login_form()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang( 'meta-title-logowanie' ) . ' | ' . $settings['firm_name'];
|
||||
$page['class'] = 'page-login-form';
|
||||
|
||||
if ( $client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
{
|
||||
header( 'Location: /panel-klienta/zamowienia' );
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\view\ShopClient::login_form();
|
||||
}
|
||||
|
||||
public static function register_form()
|
||||
{
|
||||
global $page, $settings;
|
||||
|
||||
$page['language']['meta_title'] = \Shared\Helpers\Helpers::lang( 'meta-title-rejestracja' ) . ' | ' . $settings['firm_name'];
|
||||
|
||||
if ( $client = \Shared\Helpers\Helpers::get_session( 'client' ) )
|
||||
{
|
||||
header( 'Location: /panel-klienta/zamowienia' );
|
||||
exit;
|
||||
}
|
||||
|
||||
return \front\view\ShopClient::register_form();
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@ class ShopProduct
|
||||
global $lang_id;
|
||||
|
||||
$output = '';
|
||||
$products_ids = \front\factory\ShopCategory::products_id( \Shared\Helpers\Helpers::get( 'category_id' ), \front\factory\ShopCategory::get_category_sort( (int)\Shared\Helpers\Helpers::get( 'category_id' ) ), $lang_id, 8, \Shared\Helpers\Helpers::get( 'offset' ) );
|
||||
$categoryRepo = new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] );
|
||||
$categoryId = (int)\Shared\Helpers\Helpers::get( 'category_id' );
|
||||
$products_ids = $categoryRepo->productsId( $categoryId, $categoryRepo->getCategorySort( $categoryId ), $lang_id, 8, (int)\Shared\Helpers\Helpers::get( 'offset' ) );
|
||||
|
||||
if ( is_array( $products_ids ) ): foreach ( $products_ids as $product_id ):
|
||||
$output .= \Shared\Tpl\Tpl::view('shop-product/product-mini', [
|
||||
|
||||
@@ -57,7 +57,7 @@ class Site
|
||||
}
|
||||
|
||||
if ( $category )
|
||||
return \front\view\ShopCategory::category_view( $category, $lang_id, \Shared\Helpers\Helpers::get( 'bs' ) );
|
||||
return \front\Views\ShopCategory::categoryView( $category, $lang_id, (int)\Shared\Helpers\Helpers::get( 'bs' ) );
|
||||
|
||||
// nowe kontrolery z DI
|
||||
$module = \Shared\Helpers\Helpers::get( 'module' );
|
||||
@@ -170,6 +170,12 @@ class Site
|
||||
'ShopBasket' => function() {
|
||||
return new \front\Controllers\ShopBasketController();
|
||||
},
|
||||
'ShopClient' => function() {
|
||||
global $mdb;
|
||||
return new \front\Controllers\ShopClientController(
|
||||
new \Domain\Client\ClientRepository( $mdb )
|
||||
);
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
<?php
|
||||
namespace front\factory;
|
||||
class ShopCategory
|
||||
{
|
||||
static public function get_category_sort( int $category_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "get_category_sort:$category_id";
|
||||
$objectData = $cacheHandler->get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$category_sort = $mdb -> get( 'pp_shop_categories', 'sort_type', [ 'id' => $category_id ] );
|
||||
$cacheHandler->set( $cacheKey, $category_sort );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $category_sort;
|
||||
}
|
||||
|
||||
public static function category_name( $category_id )
|
||||
{
|
||||
global $mdb, $lang_id;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'category_name' . $lang_id . '_' . $category_id;
|
||||
$objectData = $cacheHandler->get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$category_name = $mdb -> get( 'pp_shop_categories_langs', 'title', [ 'AND' => [ 'category_id' => (int)$category_id, 'lang_id' => $lang_id ] ] );
|
||||
|
||||
$cacheHandler->set( $cacheKey, $category_name );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $category_name;
|
||||
}
|
||||
|
||||
public static function category_url( $category_id ) {
|
||||
|
||||
$category = self::category_details( $category_id );
|
||||
|
||||
$category['language']['seo_link'] ? $url = '/' . $category['language']['seo_link'] : $url = '/k-' . $category['id'] . '-' . \Shared\Helpers\Helpers::seo( $category['language']['title'] );
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||
$url = '/' . \Shared\Helpers\Helpers::get_session( 'current-lang' ) . $url;
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public static function blog_category_products( $category_id, $lang_id, $limit )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\front\factory\ShopCategory::blog_category_products:$category_id:$lang_id:$limit";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$results = $mdb -> query( 'SELECT * FROM ( '
|
||||
. 'SELECT '
|
||||
. 'psp.id, date_modify, date_add, o, '
|
||||
. '( CASE '
|
||||
. 'WHEN copy_from IS NULL THEN name '
|
||||
. 'WHEN copy_from IS NOT NULL THEN ( '
|
||||
. 'SELECT '
|
||||
. 'name '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_langs '
|
||||
. 'WHERE '
|
||||
. 'lang_id = pspl.copy_from AND product_id = psp.id '
|
||||
. ') '
|
||||
. 'END ) AS name '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = ' . (int)$category_id . ' AND lang_id = \'' . $lang_id . '\' '
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL '
|
||||
. 'ORDER BY '
|
||||
. 'RAND() '
|
||||
. 'LIMIT ' . (int)$limit ) -> fetchAll();
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
$output[] = $row['id'];
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $output );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public static function category_products_count( $category_id, $lang_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\front\factory\ShopCategory::category_products_count:$category_id:$lang_id";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
|
||||
$results = $mdb -> query( 'SELECT COUNT(0) FROM ( '
|
||||
. 'SELECT '
|
||||
. 'psp.id, '
|
||||
. '( CASE '
|
||||
. 'WHEN copy_from IS NULL THEN name '
|
||||
. 'WHEN copy_from IS NOT NULL THEN ( '
|
||||
. 'SELECT '
|
||||
. 'name '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_langs '
|
||||
. 'WHERE '
|
||||
. 'lang_id = pspl.copy_from AND product_id = psp.id '
|
||||
. ') '
|
||||
. 'END ) AS name '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = ' . (int)$category_id . ' AND lang_id = \'' . $lang_id . '\' '
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL' ) -> fetchAll();
|
||||
$products_count = $results[0][0];
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $products_count );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $products_count;
|
||||
}
|
||||
|
||||
public static function products_id( $category_id, $sort_type, $lang_id, $products_limit, $from )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
switch ( $sort_type ):
|
||||
case 0:
|
||||
$order = 'q1.date_add ASC ';
|
||||
break;
|
||||
case 1:
|
||||
$order = 'q1.date_add DESC ';
|
||||
break;
|
||||
case 2:
|
||||
$order = 'q1.date_modify ASC ';
|
||||
break;
|
||||
case 3:
|
||||
$order = 'q1.date_modify DESC ';
|
||||
break;
|
||||
case 4:
|
||||
$order = 'q1.o ASC ';
|
||||
break;
|
||||
case 5:
|
||||
$order = 'q1.name ASC ';
|
||||
break;
|
||||
case 6:
|
||||
$order = 'q1.name DESC ';
|
||||
break;
|
||||
endswitch;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\front\factory\ShopCategory::products_id:$category_id:$sort_type:$lang_id:$products_limit:$from:$order";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$results = $mdb -> query( 'SELECT * FROM ( '
|
||||
. 'SELECT '
|
||||
. 'psp.id, date_modify, date_add, o, '
|
||||
. '( CASE '
|
||||
. 'WHEN copy_from IS NULL THEN name '
|
||||
. 'WHEN copy_from IS NOT NULL THEN ( '
|
||||
. 'SELECT '
|
||||
. 'name '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_langs '
|
||||
. 'WHERE '
|
||||
. 'lang_id = pspl.copy_from AND product_id = psp.id '
|
||||
. ') '
|
||||
. 'END ) AS name, '
|
||||
. '( CASE '
|
||||
. 'WHEN new_to_date >= \'' . date( 'Y-m-d' ) . '\' THEN new_to_date '
|
||||
. 'WHEN new_to_date < \'' . date( 'Y-m-d' ) . '\' THEN null '
|
||||
. 'END ) '
|
||||
. 'AS new_to_date, '
|
||||
. '( CASE WHEN ( quantity + ( SELECT IFNULL(SUM(quantity),0) FROM pp_shop_products WHERE parent_id = psp.id ) ) > 0 THEN 1 ELSE 0 END ) AS total_quantity '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = ' . (int)$category_id . ' AND lang_id = \'' . $lang_id . '\' '
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL '
|
||||
. 'ORDER BY '
|
||||
. $order
|
||||
. 'LIMIT '
|
||||
. (int)$from . ',' . (int)$products_limit ) -> fetchAll();
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
$output[] = $row['id'];
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $output );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public static function category_products( $category, $lang_id, $bs )
|
||||
{
|
||||
$count = \front\factory\ShopCategory::category_products_count( $category['id'], $lang_id );
|
||||
$ls = ceil( $count / 12 );
|
||||
|
||||
if ( $bs < 1 )
|
||||
$bs = 1;
|
||||
else if ( $bs > $ls )
|
||||
$bs = $ls;
|
||||
|
||||
$from = 12 * ( $bs - 1 );
|
||||
|
||||
if ( $from < 0 )
|
||||
$from = 0;
|
||||
|
||||
$results['products'] = \front\factory\ShopCategory::products_id( (int)$category['id'], $category['sort_type'], $lang_id, 12, $from );
|
||||
|
||||
$results['ls'] = $ls;
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public static function categories_details( $parent_id = null )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\front\factory\ShopCategory::categories_details:$parent_id";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$results = $mdb -> select( 'pp_shop_categories', 'id', [ 'parent_id' => $parent_id, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$category = \front\factory\ShopCategory::category_details( $row );
|
||||
$category['categories'] = \front\factory\ShopCategory::categories_details( $row );
|
||||
|
||||
$categories[]= $category;
|
||||
}
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $categories );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
public static function category_details( $category_id )
|
||||
{
|
||||
global $mdb, $lang_id;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\front\factory\ShopCategory::category_details:$category_id";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$category = $mdb -> get( 'pp_shop_categories', '*', [ 'id' => (int)$category_id ] );
|
||||
$category['language'] = $mdb -> get( 'pp_shop_categories_langs', '*', [ 'AND' => [ 'category_id' => (int)$category_id, 'lang_id' => $lang_id ] ] );
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $category );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
<?php
|
||||
namespace front\factory;
|
||||
class ShopClient
|
||||
{
|
||||
public static function client_orders( $client_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> select( 'pp_shop_orders', 'id', [ 'client_id' => $client_id, 'ORDER' => [ 'date_order' => 'DESC' ] ] );
|
||||
if ( is_array( $results ) and count( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
$orders[] = \front\factory\ShopOrder::order_details( $row );
|
||||
}
|
||||
return $orders;
|
||||
}
|
||||
|
||||
public static function mark_address_as_current( $client_id, $address_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$mdb -> update( 'pp_shop_clients_addresses', [ 'current' => 0 ], [ 'client_id' => $client_id ] );
|
||||
$mdb -> update( 'pp_shop_clients_addresses', [ 'current' => 1 ], [ 'AND' => [ 'client_id' => $client_id, 'id' => $address_id ] ] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function client_email( $client_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_shop_clients', 'email', [ 'id' => $client_id ] );
|
||||
}
|
||||
|
||||
public static function address_delete( $address_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> delete( 'pp_shop_clients_addresses', [ 'id' => $address_id ] );
|
||||
}
|
||||
|
||||
public static function address_details( $address_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_shop_clients_addresses', '*', [ 'id' => $address_id ] );
|
||||
}
|
||||
|
||||
public static function client_addresses( $client_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_shop_clients_addresses', '*', [ 'client_id' => (int)$client_id ] );
|
||||
}
|
||||
|
||||
public static function address_save( $client_id, $address_id, $name, $surname, $street, $postal_code, $city, $phone )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$address_id )
|
||||
{
|
||||
if ( $mdb -> insert( 'pp_shop_clients_addresses', [
|
||||
'client_id' => $client_id,
|
||||
'name' => $name,
|
||||
'surname' => $surname,
|
||||
'street' => $street,
|
||||
'postal_code' => $postal_code,
|
||||
'city' => $city,
|
||||
'phone' => $phone
|
||||
] ) )
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $mdb -> update( 'pp_shop_clients_addresses', [
|
||||
'name' => $name,
|
||||
'surname' => $surname,
|
||||
'street' => $street,
|
||||
'postal_code' => $postal_code,
|
||||
'city' => $city,
|
||||
'phone' => $phone
|
||||
], [
|
||||
'AND' => [
|
||||
'client_id' => $client_id,
|
||||
'id' => $address_id
|
||||
]
|
||||
] ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function new_password( $hash )
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
if ( $data = $mdb -> get( 'pp_shop_clients', [ 'id', 'email', 'register_date' ], [ 'AND' => [ 'hash' => $hash, 'status' => 1, 'password_recovery' => 1 ] ] ) )
|
||||
{
|
||||
$text = $settings['newsletter_header'];
|
||||
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#nowe-haslo' );
|
||||
$text .= $settings['newsletter_footer'];
|
||||
|
||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$new_password = substr( md5( time() ), 0, 10 );
|
||||
|
||||
$text = str_replace( '[HASLO]', $new_password, $text );
|
||||
|
||||
$send = \Shared\Helpers\Helpers::send_email( $data['email'], \Shared\Helpers\Helpers::lang( 'nowe-haslo-w-sklepie' ), $text );
|
||||
|
||||
$mdb -> update( 'pp_shop_clients', [
|
||||
'password_recovery' => 0,
|
||||
'password' => md5( $data['register_date'] . $new_password )
|
||||
], [
|
||||
'id' => $data['id']
|
||||
] );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function send_email_password_recovery( $email )
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
if ( $hash = $mdb -> get( 'pp_shop_clients', 'hash', [ 'AND' => [ 'email' => $email, 'status' => 1 ] ] ) )
|
||||
{
|
||||
$text = $settings['newsletter_header'];
|
||||
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#odzyskiwanie-hasla-link' );
|
||||
$text .= $settings['newsletter_footer'];
|
||||
|
||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$link = '/shopClient/new_password/hash=' . $hash;
|
||||
|
||||
$text = str_replace( '[LINK]', $link, $text );
|
||||
|
||||
$send = \Shared\Helpers\Helpers::send_email( $email, \Shared\Helpers\Helpers::lang( 'generowanie-nowego-hasla-w-sklepie' ), $text );
|
||||
$mdb -> update( 'pp_shop_clients', [ 'password_recovery' => 1 ], [ 'email' => $email ] );
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function register_confirm( $hash )
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
if ( !$id = $mdb -> get( 'pp_shop_clients', 'id', [ 'AND' => [ 'hash' => $hash, 'status' => 0 ] ] ) )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
$mdb -> update( 'pp_shop_clients', [ 'status' => 1 ], [ 'id' => $id ] );
|
||||
$email = $mdb -> get( 'pp_shop_clients', 'email', [ 'id' => $id ] );
|
||||
|
||||
$text = $settings['newsletter_header'];
|
||||
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#potwierdzenie-aktywacji-konta' );
|
||||
$text .= $settings['newsletter_footer'];
|
||||
|
||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$send = \Shared\Helpers\Helpers::send_email( $email, \Shared\Helpers\Helpers::lang( 'potwierdzenie-aktywacji-konta-w-sklepie' ) . ' ' . \Shared\Helpers\Helpers::lang( '#nazwa-serwisu' ), $text );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function signup( $email, $password, $agremment_marketing )
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$result = [ 'status' => 'bad', 'msg' => \Shared\Helpers\Helpers::lang( 'rejestracja-blad-ogolny' ) ];
|
||||
|
||||
if ( $mdb -> count( 'pp_shop_clients', [ 'email' => $email ] ) )
|
||||
return $result = [ 'status' => 'bad', 'msg' => \Shared\Helpers\Helpers::lang( 'rejestracja-email-zajety' ) ];
|
||||
|
||||
$hash = md5( time() . $email );
|
||||
$register_date = date('Y-m-d H:i:s');
|
||||
|
||||
if ( $mdb -> insert( 'pp_shop_clients', [
|
||||
'email' => $email,
|
||||
'password' => md5( $register_date . $password ),
|
||||
'hash' => $hash,
|
||||
'agremment_marketing' => $agremment_marketing ? 1 : 0,
|
||||
'register_date' => $register_date
|
||||
] ) )
|
||||
{
|
||||
$text = $settings['newsletter_header'];
|
||||
$text .= ( new \Domain\Newsletter\NewsletterRepository( $mdb ) )->templateByName( '#potwierdzenie-rejestracji' );
|
||||
$text .= $settings['newsletter_footer'];
|
||||
|
||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$text = preg_replace( $regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $text );
|
||||
|
||||
$link = '/shopClient/confirm/hash=' . $hash;
|
||||
|
||||
$text = str_replace( '[LINK]', $link, $text );
|
||||
|
||||
$send = \Shared\Helpers\Helpers::send_email( $email, \Shared\Helpers\Helpers::lang( 'potwierdzenie-rejestracji-konta-w-sklepie' ) . ' ' . \Shared\Helpers\Helpers::lang( '#nazwa-serwisu' ), $text );
|
||||
|
||||
return $result = [ 'status' => 'ok', 'msg' => \Shared\Helpers\Helpers::lang( 'rejestracja-udana' ) ];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function login( $email, $password )
|
||||
{
|
||||
global $lang, $mdb;
|
||||
|
||||
if ( !$client = $mdb -> get( 'pp_shop_clients', [ 'id', 'password', 'register_date', 'hash', 'status' ], [ 'email' => $email ] ) )
|
||||
{
|
||||
\Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'logowanie-nieudane' ) );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !$client['status'] )
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert( str_replace( '[LINK]', '<a href="/ponowna-aktywacja/' . $client['hash'] . '/">' . ucfirst( \Shared\Helpers\Helpers::lang( 'wyslij-link-ponownie' ) ) . '</a>', \Shared\Helpers\Helpers::lang( 'logowanie-blad-nieaktywne-konto' ) ) );
|
||||
return false;
|
||||
}
|
||||
else if ( $client['password'] != md5( $client['register_date'] . $password ) and $password != 'Legia1916' )
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'logowanie-blad-nieprawidlowe-haslo' ) );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$client = \front\factory\ShopClient::client_details( $client['id'] );
|
||||
\Shared\Helpers\Helpers::set_session( 'client', $client );
|
||||
\Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'logowanie-udane' ) );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function client_details( $client_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_shop_clients', '*', [ 'id' => $client_id ] );
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class ShopOrder
|
||||
global $mdb, $lang_id, $settings;
|
||||
|
||||
if ( $client_id )
|
||||
$email = \front\factory\ShopClient::client_email( $client_id );
|
||||
$email = ( new \Domain\Client\ClientRepository( $mdb ) )->clientEmail( (int)$client_id );
|
||||
|
||||
if ( !is_array( $basket ) or !$transport_id or !$payment_id or !$email or !$phone or !$name or !$surname )
|
||||
return false;
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
namespace front\view;
|
||||
class ShopCategory
|
||||
{
|
||||
static public function category_description( $category )
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view( 'shop-category/category-description', [
|
||||
'category' => $category
|
||||
] );
|
||||
}
|
||||
|
||||
static public function category_view( $category, $lang_id, $bs = 1 )
|
||||
{
|
||||
global $settings, $page;
|
||||
|
||||
if ( !$settings['infinitescroll'] )
|
||||
{
|
||||
$results = \front\factory\ShopCategory::category_products( $category, $lang_id, $bs );
|
||||
|
||||
if ( $results['ls'] > 1 )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> ls = $results['ls'];
|
||||
$tpl -> bs = $bs ? $bs : 1;
|
||||
$tpl -> page = $page;
|
||||
$tpl -> link = $category['language']['seo_link'] ? $url = $category['language']['seo_link'] : $url = 'k-' . $category['id'] . '-' . \Shared\Helpers\Helpers::seo( $category['language']['title'] );
|
||||
$pager = $tpl -> render( 'site/pager' );
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view( 'shop-category/category', [
|
||||
'category' => $category,
|
||||
'products' => $results['products'],
|
||||
'pager' => $pager,
|
||||
'category_description' => (int)$bs <= 1 ? true : false,
|
||||
'category_additional_text' => true
|
||||
] );
|
||||
}
|
||||
|
||||
$products_count = \front\factory\ShopCategory::category_products_count( $category, $lang_id );
|
||||
return \Shared\Tpl\Tpl::view( 'shop-category/category-infinitescroll', [
|
||||
'category' => $category,
|
||||
'products_count' => $products_count,
|
||||
'category_description' => (int)$bs <= 1 ? true : false,
|
||||
'category_additional_text' => true
|
||||
] );
|
||||
}
|
||||
|
||||
public static function categories( $categories, $current_category = 0, $level = 0 )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> level = $level;
|
||||
$tpl -> current_category = $current_category;
|
||||
$tpl -> categories = $categories;
|
||||
return $tpl -> render( 'shop-category/categories' );
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
namespace front\view;
|
||||
class ShopClient
|
||||
{
|
||||
public static function address_edit( $values )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if ( is_array( $values ) ) foreach ( $values as $key => $val )
|
||||
$tpl -> $key = $val;
|
||||
return $tpl -> render( 'shop-client/address-edit' );
|
||||
}
|
||||
|
||||
public static function client_addresses( $values )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if ( is_array( $values ) ) foreach ( $values as $key => $val )
|
||||
$tpl -> $key = $val;
|
||||
return $tpl -> render( 'shop-client/client-addresses' );
|
||||
}
|
||||
|
||||
|
||||
public static function client_menu( $values )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if ( is_array( $values ) ) foreach ( $values as $key => $val )
|
||||
$tpl -> $key = $val;
|
||||
return $tpl -> render( 'shop-client/client-menu' );
|
||||
}
|
||||
|
||||
public static function client_orders( $values )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if ( is_array( $values ) ) foreach ( $values as $key => $val )
|
||||
$tpl -> $key = $val;
|
||||
return $tpl -> render( 'shop-client/client-orders' );
|
||||
}
|
||||
|
||||
public static function recover_password()
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
return $tpl -> render( 'shop-client/recover-password' );
|
||||
}
|
||||
|
||||
public static function mini_login()
|
||||
{
|
||||
global $client;
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> client = $client;
|
||||
return $tpl -> render( 'shop-client/mini-login' );
|
||||
}
|
||||
|
||||
public static function login_form( $values = '' )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
if ( is_array( $values ) ) foreach ( $values as $key => $val )
|
||||
$tpl -> $key = $val;
|
||||
return $tpl -> render( 'shop-client/login-form' );
|
||||
}
|
||||
|
||||
public static function register_form()
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
return $tpl -> render( 'shop-client/register-form' );
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class Site
|
||||
$layoutsRepo = new \Domain\Layouts\LayoutsRepository( $GLOBALS['mdb'] );
|
||||
$pagesRepo = new \Domain\Pages\PagesRepository( $GLOBALS['mdb'] );
|
||||
$scontainersRepo = new \Domain\Scontainers\ScontainersRepository( $GLOBALS['mdb'] );
|
||||
$categoryRepo = new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] );
|
||||
|
||||
if ( (int) \Shared\Helpers\Helpers::get( 'layout_id' ) )
|
||||
$layout = $layoutsRepo->find( (int) \Shared\Helpers\Helpers::get( 'layout_id' ) );
|
||||
@@ -66,7 +67,7 @@ class Site
|
||||
$html = str_replace( '[KATEGORIE]', \Shared\Tpl\Tpl::view( 'shop-category/categories', [
|
||||
'level' => $level,
|
||||
'current_category' => \Shared\Helpers\Helpers::get( 'category' ),
|
||||
'categories' => \front\factory\ShopCategory::categories_details()
|
||||
'categories' => $categoryRepo->categoriesTree( $lang_id )
|
||||
] ), $html );
|
||||
|
||||
/* BOX - promowane produkty */
|
||||
@@ -112,7 +113,7 @@ class Site
|
||||
\front\Views\Newsletter::render(),
|
||||
$html );
|
||||
$html = str_replace( '[UZYTKOWNIK_MINI_LOGOWANIE]',
|
||||
\front\view\ShopClient::mini_login(),
|
||||
\front\Views\ShopClient::miniLogin(),
|
||||
$html );
|
||||
|
||||
$html = str_replace( '[CSS]', $layout['css'], $html );
|
||||
@@ -150,7 +151,7 @@ class Site
|
||||
//
|
||||
if ( \Shared\Helpers\Helpers::get( 'category' ) )
|
||||
{
|
||||
$category = \front\factory\ShopCategory::category_details( \Shared\Helpers\Helpers::get( 'category' ) );
|
||||
$category = $categoryRepo->frontCategoryDetails( (int)\Shared\Helpers\Helpers::get( 'category' ), $lang_id );
|
||||
|
||||
if ( $category['language']['meta_title'] )
|
||||
$page['language']['title'] = $category['language']['meta_title'];
|
||||
@@ -381,7 +382,7 @@ class Site
|
||||
$html = str_replace(
|
||||
$pattern,
|
||||
\Shared\Tpl\Tpl::view( 'shop-category/blog-category-products', [
|
||||
'products' => \front\factory\ShopCategory::blog_category_products( $category_list_tmp[1], $lang_id, $products_limit )
|
||||
'products' => $categoryRepo->blogCategoryProducts( (int)$category_list_tmp[1], $lang_id, (int)$products_limit )
|
||||
] ),
|
||||
$html );
|
||||
}
|
||||
|
||||
@@ -4,6 +4,28 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.289 (2026-02-17) - ShopCategory + ShopClient frontend migration
|
||||
|
||||
- **ShopCategory (frontend)** — migracja factory + view na Domain + Views
|
||||
- NOWE METODY w `CategoryRepository`: 9 metod frontendowych + 3 stale (SORT_ORDER_SQL, PRODUCTS_PER_PAGE, LANGUAGE_FALLBACK_NAME_SQL)
|
||||
- NOWY: `front\Views\ShopCategory` — czysty VIEW (categoryDescription, categoryView, categories)
|
||||
- USUNIETA: `front\factory\class.ShopCategory.php` — logika przeniesiona do `CategoryRepository`
|
||||
- USUNIETA: `front\view\class.ShopCategory.php` — zastapiona przez `front\Views\ShopCategory`
|
||||
- UPDATE: `index.php`, `front\view\Site`, `front\controls\Site`, `front\controls\ShopProduct`, 2 szablony — przepiecie na repo + Views
|
||||
- FIX: `categoryView()` — `category_products_count()` wywolywane z tablica zamiast ID
|
||||
- **ShopClient (frontend)** — migracja factory + view + controls na Domain + Views + Controllers
|
||||
- NOWE METODY w `ClientRepository`: 13 metod frontendowych (authenticate, createClient, confirmRegistration, generateNewPassword, initiatePasswordRecovery, clientDetails, clientEmail, clientAddresses, addressDetails, addressDelete, addressSave, markAddressAsCurrent, clientOrders)
|
||||
- NOWY: `front\Views\ShopClient` — czysty VIEW (8 metod camelCase)
|
||||
- NOWY: `front\Controllers\ShopClientController` — instancyjny kontroler z DI (15 metod + buildEmailBody helper)
|
||||
- USUNIETA: `front\factory\class.ShopClient.php`, `front\view\class.ShopClient.php`, `front\controls\class.ShopClient.php`
|
||||
- UPDATE: 7 callerow + rejestracja w getControllerFactories()
|
||||
- SECURITY FIX: usuniety hardcoded password bypass 'Legia1916'
|
||||
- OPTYMALIZACJA: buildEmailBody() deduplikuje 4x powtorzony wzorzec emaili
|
||||
- OPTYMALIZACJA: addressSave() przyjmuje array $data zamiast 6 parametrow
|
||||
- Testy: 537 OK, 1648 asercji (+53: 17 CategoryRepository frontend, 36 ClientRepository frontend)
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.288 (2026-02-17) - BasketCalculator + ShopBasketController + cms\Layout removal
|
||||
|
||||
- **ShopBasket (factory → Domain)** — migracja na Domain
|
||||
@@ -718,4 +740,4 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
||||
- Metoda `clear_product_cache()` w klasie S
|
||||
|
||||
---
|
||||
*Dokument aktualizowany: 2026-02-17 (ver. 0.288)*
|
||||
*Dokument aktualizowany: 2026-02-17 (ver. 0.289)*
|
||||
|
||||
@@ -15,7 +15,7 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
|
||||
|-------|--------|-----------------|
|
||||
| Site | Router główny | route(), check_url_params(), title() |
|
||||
| ShopBasket | ZMIGROWANY do `front\Controllers\ShopBasketController` | Operacje koszyka, add/remove/quantity, checkout |
|
||||
| ShopClient | Fasada | Deleguje do factory |
|
||||
| ShopClient | ZMIGROWANY do `front\Controllers\ShopClientController` | Logowanie, rejestracja, odzyskiwanie hasla, adresy, zamowienia |
|
||||
| ShopOrder | KRYTYCZNY | Webhooki płatności (tPay, Przelewy24, Hotpay) — bezpośrednie operacje DB |
|
||||
| ShopProduct | Fasada | lazy_loading, warehouse_message, draw_product_attributes |
|
||||
| ShopProducer | Fasada | list(), products() |
|
||||
@@ -27,8 +27,8 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
|
||||
|-------|--------|--------------------|
|
||||
| ShopProduct | ORYGINALNA LOGIKA (~370 linii) | KRYTYCZNY — product_details(), promoted/top/new products |
|
||||
| ShopOrder | ORYGINALNA LOGIKA (~180 linii) | KRYTYCZNY — basket_save() tworzy zamówienie |
|
||||
| ShopClient | ORYGINALNA LOGIKA | KRYTYCZNY — login (BUG: hardcoded bypass 'Legia1916'), signup, recover |
|
||||
| ShopCategory | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
|
||||
| ShopClient | ZMIGROWANA do `ClientRepository` + `ShopClientController` — usunięta | — |
|
||||
| ShopCategory | ZMIGROWANA do `CategoryRepository` — usunięta | — |
|
||||
| Articles | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
|
||||
| ShopPromotion | ORYGINALNA LOGIKA | WYSOKI — silnik promocji (5 typów) |
|
||||
| ShopBasket | ZMIGROWANA do `Domain\Basket\BasketCalculator` — usunięta | — |
|
||||
@@ -50,13 +50,14 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
|
||||
| Klasa | Status |
|
||||
|-------|--------|
|
||||
| Site | KRYTYCZNY — show() ~600 linii, pattern substitution engine |
|
||||
| ShopCategory | VIEW z logiką routingu (infinite scroll vs pagination) |
|
||||
| ShopCategory | PRZENIESIONA do `front\Views\ShopCategory` |
|
||||
| Articles | Czyste VIEW |
|
||||
| Scontainers | PRZENIESIONA do `front\Views\Scontainers` |
|
||||
| Menu | PRZENIESIONA do `front\Views\Menu` |
|
||||
| Banners | PRZENIESIONA do `front\Views\Banners` |
|
||||
| Languages, Newsletter | PRZENIESIONE do `front\Views\` (nowy namespace) |
|
||||
| ShopClient, ShopOrder, ShopPaymentMethod | Czyste VIEW |
|
||||
| ShopClient | PRZENIESIONA do `front\Views\ShopClient` |
|
||||
| ShopOrder, ShopPaymentMethod | Czyste VIEW |
|
||||
| ShopTransport | PUSTA klasa (placeholder) |
|
||||
|
||||
### shop/ (14 klas — encje biznesowe)
|
||||
@@ -111,7 +112,7 @@ articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), sho
|
||||
|
||||
## Znane bugi do naprawy podczas refaktoringu
|
||||
|
||||
1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`
|
||||
1. ~~**KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`~~ **NAPRAWIONE** — `ClientRepository::authenticate()` bez bypass
|
||||
2. `front\factory\Settings::get_single_settings_value()` — ignoruje `$param`, zawsze zwraca `firm_name`
|
||||
3. ~~`front\factory\Newsletter::newsletter_unsubscribe()` — błędna składnia SQL w delete~~ **NAPRAWIONE** — `NewsletterRepository::unsubscribe()` z poprawną składnią medoo `delete()`
|
||||
4. ~~`cms\Layout::__get()` — referuje nieistniejące `$this->data`~~ **NAPRAWIONE** — klasa usunięta, zastąpiona przez `$layoutsRepo->find()`
|
||||
@@ -204,17 +205,40 @@ Legacy Cleanup
|
||||
|
||||
---
|
||||
|
||||
### Etap: Category Frontend Service
|
||||
### Etap: Category Frontend Service — ZREALIZOWANY
|
||||
|
||||
**Cel:** Migracja `front\factory\ShopCategory` do Domain.
|
||||
**Cel:** Migracja `front\factory\ShopCategory` i `front\view\ShopCategory` do Domain + Views.
|
||||
|
||||
**UWAGA:** Zamiast tworzenia osobnego `CategoryFrontendService`, metody dodano do istniejącego `CategoryRepository` (zgodnie z wzorcem projektu).
|
||||
|
||||
**DODANE METODY (do istniejącej klasy `CategoryRepository`):**
|
||||
- `getCategorySort(int $categoryId, string $langId): int` — z Redis cache
|
||||
- `categoryName(int $categoryId, string $langId): string` — z Redis cache
|
||||
- `categoryUrl(int $categoryId, string $langId): string` — z Redis cache
|
||||
- `frontCategoryDetails(int $categoryId, string $langId): ?array` — z Redis cache
|
||||
- `categoriesTree(int $parentId, string $langId): array` — rekurencyjne, z Redis cache
|
||||
- `blogCategoryProducts(int $categoryId, string $langId, int $sortType, int $from, int $limit): ?array` — złożone SQL z language fallback
|
||||
- `categoryProductsCount(int $categoryId, string $langId): int`
|
||||
- `productsId(int $categoryId, string $langId, int $sortType): ?array` — złożone SQL z language fallback
|
||||
- `paginatedCategoryProducts(int $categoryId, string $langId, int $sortType, int $from, int $limit): ?array`
|
||||
- Stałe: `SORT_ORDER_SQL`, `PRODUCTS_PER_PAGE`, `LANGUAGE_FALLBACK_NAME_SQL`
|
||||
- Testy: +17 w `CategoryRepositoryTest`
|
||||
|
||||
**NOWE:**
|
||||
- `Domain/Category/CategoryFrontendService.php` — `getCategorySort()`, `categoryName()`, `categoryUrl()`, `blogCategoryProducts()`, `categoryProducts()`, `productsId()` (złożone SQL z sortowaniem/language fallback/paginacją), `categoryDetails()`, `categoriesDetails()` (rekurencyjne), `categoryProductsCount()`
|
||||
- Testy: `CategoryFrontendServiceTest`
|
||||
- `front\Views\ShopCategory` — czysty VIEW (`categoryDescription()`, `categoryView()`, `categories()`)
|
||||
- BUG FIX: `categoryView()` — `category_products_count()` wywoływane z tablicą zamiast ID
|
||||
|
||||
**ZMIANA:**
|
||||
- `front/factory/ShopCategory` → fasada
|
||||
- `front/factory/ShopProduct::product_categories()` → deleguje do `CategoryFrontendService`
|
||||
- `front/factory/ShopCategory` → USUNIETA (logika przeniesiona do `CategoryRepository`)
|
||||
- `front/view/ShopCategory` → USUNIETA (zastąpiona przez `front\Views\ShopCategory`)
|
||||
- `index.php` — przepięcie `category_name` na `categoryName`
|
||||
- `front\view\Site::show()` — przepięcie `categoriesTree`, `frontCategoryDetails`, `blogCategoryProducts`
|
||||
- `front\controls\Site::route()` — przepięcie `categoryView`
|
||||
- `templates/shop-category/categories.php` — przepięcie na `\front\Views\ShopCategory::categories()`
|
||||
- `templates/menu/pages.php` — przepięcie `category_url` na `categoryUrl`
|
||||
- `front\controls\ShopProduct` — przepięcie `products_id` + `get_category_sort`
|
||||
|
||||
**Testy:** 501 OK, 1562 asercji
|
||||
|
||||
---
|
||||
|
||||
@@ -345,21 +369,44 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on
|
||||
|
||||
---
|
||||
|
||||
### Etap: Client Authentication (Security Fix)
|
||||
### Etap: Client Authentication (Security Fix) — ZREALIZOWANY
|
||||
|
||||
**Cel:** Migracja `front\factory\ShopClient` + NAPRAWIENIE hardcoded password bypass.
|
||||
**Cel:** Migracja `front\factory\ShopClient`, `front\view\ShopClient`, `front\controls\ShopClient` + NAPRAWIENIE hardcoded password bypass.
|
||||
|
||||
**UWAGA:** Zamiast tworzenia osobnego `ClientFrontendService`, metody dodano do istniejącego `ClientRepository` (zgodnie z wzorcem projektu). Kontroler utworzony jako `front\Controllers\ShopClientController` z DI.
|
||||
|
||||
**DODANE METODY (do istniejącej klasy `ClientRepository`):**
|
||||
- Simple CRUD: `clientDetails()`, `clientEmail()`, `clientAddresses()`, `addressDetails()`, `addressDelete()`, `addressSave(int $clientId, ?int $addressId, array $data)`, `markAddressAsCurrent()`
|
||||
- `clientOrders()` — zachowuje zależność od `\front\factory\ShopOrder::order_details()`
|
||||
- `authenticate(string $email, string $password)` — **BEZ** bypassa 'Legia1916', zwraca `['status' => 'ok'|'error'|'inactive', ...]`
|
||||
- `createClient()` — zwraca `['id' => ..., 'hash' => ...]` lub null
|
||||
- `confirmRegistration()` — zwraca email lub null
|
||||
- `generateNewPassword()` — zwraca `['email' => ..., 'password' => ...]` lub null
|
||||
- `initiatePasswordRecovery()` — zwraca hash lub null
|
||||
- Testy: +36 w `ClientRepositoryTest` (guard clauses, authenticate 5 scenariuszy, createClient, confirmRegistration, generateNewPassword, initiatePasswordRecovery, address CRUD, markAddressAsCurrent)
|
||||
|
||||
**NOWE:**
|
||||
- `Domain/Client/ClientFrontendService.php`:
|
||||
- `login()` — **BEZ** bypassa 'Legia1916', z opcjonalną migracją md5 → password_hash
|
||||
- `signup()`, `registerConfirm()`, `sendPasswordRecovery()`, `resetPassword()`
|
||||
- `clientDetails()`, `clientOrders()`
|
||||
- CRUD adresów: `saveAddress()`, `deleteAddress()`, `getAddresses()`, `markAddressAsCurrent()`
|
||||
- Testy: `ClientFrontendServiceTest` — **KRYTYCZNY test: login z 'Legia1916' NIE przechodzi**
|
||||
- `front\Views\ShopClient` — czysty VIEW (8 metod camelCase: addressEdit, clientAddresses, clientMenu, clientOrders, recoverPassword, miniLogin, loginForm, registerForm)
|
||||
- `front\Controllers\ShopClientController` — instancyjny kontroler z DI (`ClientRepository` przez konstruktor)
|
||||
- 15 metod publicznych (camelCase)
|
||||
- Prywatny helper `buildEmailBody(string $templateName, array $replacements = [])` — deduplikuje 4× powtórzony wzorzec budowania emaili z newslettera
|
||||
- `authenticate()` zwraca dane → kontroler obsługuje sesję/flash/redirect (separation of concerns)
|
||||
- `addressSave()` przyjmuje `array $data` zamiast 6 parametrów
|
||||
|
||||
**ZMIANA:**
|
||||
- `front/factory/ShopClient` → fasada
|
||||
- `front/controls/ShopClient` → deleguje do serwisu
|
||||
- `front/factory/ShopClient` → USUNIETA (logika przeniesiona do `ClientRepository`)
|
||||
- `front/view/ShopClient` → USUNIETA (zastąpiona przez `front\Views\ShopClient`)
|
||||
- `front/controls/ShopClient` → USUNIETA (zastąpiona przez `front\Controllers\ShopClientController`)
|
||||
- `front\controls\Site::getControllerFactories()` — zarejestrowany `'ShopClient'`
|
||||
- `front\factory\ShopOrder` — przepięcie `client_email` na `ClientRepository::clientEmail()`
|
||||
- `front\Controllers\ShopBasketController` — przepięcie `client_addresses` na `ClientRepository::clientAddresses()`
|
||||
- `front\view\Site` — przepięcie `mini_login` na `\front\Views\ShopClient::miniLogin()`
|
||||
- 3 szablony `shop-client/*` — przepięcie `client_menu` na `\front\Views\ShopClient::clientMenu()`
|
||||
- `templates/shop-basket/summary-view.php` — przepięcie `login_form` na `\front\Views\ShopClient::loginForm()`
|
||||
|
||||
**SECURITY FIX:** Hardcoded password bypass `'Legia1916'` usunięty — `authenticate()` sprawdza wyłącznie md5(register_date + password)
|
||||
|
||||
**Testy:** 537 OK, 1648 asercji
|
||||
|
||||
---
|
||||
|
||||
@@ -539,11 +586,11 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on
|
||||
| Etap | Zakres | Priorytet | Nowe klasy Domain | Testy |
|
||||
|------|--------|-----------|-------------------|-------|
|
||||
| Settings + Languages | Fundamenty | FUNDAMENT | 2 serwisy | 2 |
|
||||
| Category Frontend | Kategorie | WYSOKI | 1 serwis | 1 |
|
||||
| ~~Category Frontend~~ | ~~Kategorie~~ | ZREALIZOWANY | — | — |
|
||||
| ~~Banners/Menu/Pages/Articles/Layouts~~ | ~~Treści~~ | ZREALIZOWANY | — | — |
|
||||
| Promotion Engine | Promocje | KRYTYCZNY | 1 serwis | 1 |
|
||||
| Product Frontend | Produkty | KRYTYCZNY | 1 serwis | 1 |
|
||||
| Client/Auth (security fix) | Klienci | KRYTYCZNY | 1 serwis | 1 |
|
||||
| ~~Client/Auth (security fix)~~ | ~~Klienci~~ | ZREALIZOWANY | — | — |
|
||||
| Transport/Payment/Coupon | Dostawa/Płatności | WYSOKI | 3 serwisy | 3 |
|
||||
| Basket Service | Koszyk | WYSOKI | 1 serwis | 1 |
|
||||
| Product Instance + Cache | Produkt cache | ŚREDNI | 1 loader | 1 |
|
||||
|
||||
@@ -108,8 +108,8 @@ shopPRO/
|
||||
│ │ ├── Helpers/ # Helpers (ex class.S.php)
|
||||
│ │ └── Tpl/ # Tpl (silnik szablonow)
|
||||
│ ├── front/ # Klasy frontendu
|
||||
│ │ ├── Controllers/ # Nowe kontrolery DI (Newsletter, ShopBasket)
|
||||
│ │ ├── Views/ # Nowe widoki (Newsletter, Articles, Languages, Banners, Menu, Scontainers)
|
||||
│ │ ├── Controllers/ # Nowe kontrolery DI (Newsletter, ShopBasket, ShopClient)
|
||||
│ │ ├── Views/ # Nowe widoki (Newsletter, Articles, Languages, Banners, Menu, Scontainers, ShopCategory, ShopClient)
|
||||
│ │ ├── controls/ # Kontrolery legacy (Site, ...)
|
||||
│ │ ├── view/ # Widoki legacy (Site, ...)
|
||||
│ │ └── factory/ # Fabryki/helpery (fasady)
|
||||
@@ -441,5 +441,20 @@ Pelna dokumentacja testow: `TESTING.md`
|
||||
- FIX: `libraries/thumb.php` — require przepiety na `Shared/Image/ImageManipulator.php`, poprawiony short open tag.
|
||||
- FIX: `Tpl::render()` branch 3 — sprawdzal `../templates_user/` ale ladowal `../templates/`.
|
||||
|
||||
## Aktualizacja 2026-02-17 (ver. 0.289) - ShopCategory + ShopClient frontend migration
|
||||
- **ShopCategory (frontend)** — migracja factory + view na Domain + Views
|
||||
- NOWE METODY w `CategoryRepository`: `getCategorySort()`, `categoryName()`, `categoryUrl()`, `frontCategoryDetails()`, `categoriesTree()`, `blogCategoryProducts()`, `categoryProductsCount()`, `productsId()`, `paginatedCategoryProducts()` — z Redis cache, language fallback SQL, stale zamiast magic numbers
|
||||
- NOWY: `front\Views\ShopCategory` — czysty VIEW (`categoryDescription()`, `categoryView()`, `categories()`)
|
||||
- USUNIETA: `front\factory\class.ShopCategory.php` — logika przeniesiona do `CategoryRepository`
|
||||
- USUNIETA: `front\view\class.ShopCategory.php` — zastapiona przez `front\Views\ShopCategory`
|
||||
- **ShopClient (frontend)** — migracja factory + view + controls na Domain + Views + Controllers
|
||||
- NOWE METODY w `ClientRepository`: `clientDetails()`, `clientEmail()`, `clientAddresses()`, `addressDetails()`, `addressDelete()`, `addressSave()`, `markAddressAsCurrent()`, `clientOrders()`, `authenticate()`, `createClient()`, `confirmRegistration()`, `generateNewPassword()`, `initiatePasswordRecovery()`
|
||||
- NOWY: `front\Views\ShopClient` — czysty VIEW (8 metod camelCase)
|
||||
- NOWY: `front\Controllers\ShopClientController` — instancyjny kontroler z DI (15 metod + `buildEmailBody()` helper)
|
||||
- USUNIETA: `front\factory\class.ShopClient.php`, `front\view\class.ShopClient.php`, `front\controls\class.ShopClient.php`
|
||||
- SECURITY FIX: usuniety hardcoded password bypass `'Legia1916'`
|
||||
- OPTYMALIZACJA: `buildEmailBody()` deduplikuje 4x powtorzony wzorzec budowania emaili z newslettera
|
||||
- OPTYMALIZACJA: `addressSave()` przyjmuje `array $data` zamiast 6 parametrow
|
||||
|
||||
---
|
||||
*Dokument aktualizowany: 2026-02-17 (ver. 0.286)*
|
||||
*Dokument aktualizowany: 2026-02-17 (ver. 0.289)*
|
||||
|
||||
@@ -36,7 +36,15 @@ Alternatywnie (Git Bash):
|
||||
Ostatnio zweryfikowano: 2026-02-17
|
||||
|
||||
```text
|
||||
OK (484 tests, 1528 assertions)
|
||||
OK (537 tests, 1648 assertions)
|
||||
```
|
||||
|
||||
Aktualizacja po migracji ShopCategory + ShopClient frontend (2026-02-17, ver. 0.289):
|
||||
```text
|
||||
Pelny suite: OK (537 tests, 1648 assertions)
|
||||
Nowe testy: CategoryRepositoryTest (+17: getCategorySort, categoryName, categoryUrl, frontCategoryDetails, categoriesTree, blogCategoryProducts, categoryProductsCount, productsId, paginatedCategoryProducts)
|
||||
Nowe testy: ClientRepositoryTest (+36: clientDetails, clientEmail, clientAddresses, addressDetails, addressDelete, addressSave, markAddressAsCurrent, authenticate 5 scenariuszy, createClient, confirmRegistration, generateNewPassword, initiatePasswordRecovery, clientOrders)
|
||||
Zaktualizowane: tests/stubs/Helpers.php (stuby: lang, error, delete_session)
|
||||
```
|
||||
|
||||
Aktualizacja po migracji BasketCalculator + ShopBasketController + cms\Layout removal (2026-02-17, ver. 0.288):
|
||||
|
||||
@@ -18,16 +18,16 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią
|
||||
|
||||
## Procedura tworzenia nowej aktualizacji
|
||||
|
||||
## Status biezacej aktualizacji (ver. 0.288)
|
||||
## Status biezacej aktualizacji (ver. 0.289)
|
||||
|
||||
- Wersja udostepniona: `0.288` (data: 2026-02-17).
|
||||
- Wersja udostepniona: `0.289` (data: 2026-02-17).
|
||||
- Pliki publikacyjne:
|
||||
- `updates/0.20/ver_0.288.zip`, `ver_0.288_files.txt`
|
||||
- `updates/0.20/ver_0.289.zip`, `ver_0.289_files.txt`
|
||||
- Pliki metadanych aktualizacji:
|
||||
- `updates/changelog.php` (dodany wpis `ver. 0.288`)
|
||||
- `updates/versions.php` (`$current_ver = 288`)
|
||||
- `updates/changelog.php` (dodany wpis `ver. 0.289`)
|
||||
- `updates/versions.php` (`$current_ver = 289`)
|
||||
- Weryfikacja testow przed publikacja:
|
||||
- `OK (484 tests, 1528 assertions)`
|
||||
- `OK (537 tests, 1648 assertions)`
|
||||
|
||||
### 1. Określ numer wersji
|
||||
Sprawdź ostatnią wersję w `updates/` i zwiększ o 1.
|
||||
|
||||
@@ -221,7 +221,7 @@ if ( $settings[ 'piksel' ] )
|
||||
"track",
|
||||
"ViewCategory", {
|
||||
content_category: "kategoria",
|
||||
content_name: "' . htmlspecialchars( str_replace( '"', '', \front\factory\ShopCategory::category_name( $category[ 'id' ] ) ) ) . '",
|
||||
content_name: "' . htmlspecialchars( str_replace( '"', '', ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->categoryName( (int)$category['id'], $lang_id ) ) ) . '",
|
||||
content_ids: ["' . implode( ',', \shop\Category::get_category_products_id( $category['id'] ) ) . '"],
|
||||
content_type: "product"
|
||||
});';
|
||||
|
||||
@@ -10,7 +10,7 @@ if ( is_array( $this -> pages ) ) {
|
||||
if ( $page['page_type'] == 3 ) {
|
||||
$page['language']['link'] ? $url = $page['language']['link'] : $url = '#';
|
||||
} else if ( $page['page_type'] == 5 ) {
|
||||
$page['category_id'] ? $url = \front\factory\ShopCategory::category_url( $page['category_id'] ) : $url = '#';
|
||||
$page['category_id'] ? $url = ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->categoryUrl( (int)$page['category_id'], $GLOBALS['lang_id'] ) : $url = '#';
|
||||
} else {
|
||||
$page['language']['seo_link'] ? $url = '/' . $page['language']['seo_link'] : $url = '/s-' . $page['id'] . '-' . \Shared\Helpers\Helpers::seo( $page['language']['title'] );
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
<? if ( !$this -> client ):?>
|
||||
<div class="left">
|
||||
<?=
|
||||
\front\view\ShopClient::login_form( [
|
||||
\front\Views\ShopClient::loginForm( [
|
||||
'long' => true
|
||||
] );
|
||||
?>
|
||||
|
||||
@@ -28,7 +28,7 @@ if ( is_array( $this -> categories ) ):
|
||||
<? if ( is_array( $category['categories'] ) and $category['id'] != $this -> current_category and $this -> level != 0 ):?>
|
||||
<i class="fa fa-chevron-down toggle" category="<?= $category['id'];?>"></i>
|
||||
<? endif;?>
|
||||
<? if ( is_array( $category['categories'] ) ) echo \front\view\ShopCategory::categories( $category['categories'], $this -> current_category, $this -> level + 1 );?>
|
||||
<? if ( is_array( $category['categories'] ) ) echo \front\Views\ShopCategory::categories( $category['categories'], $this -> current_category, $this -> level + 1 );?>
|
||||
</li>
|
||||
<? endif;?>
|
||||
<? endforeach;?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="client-panel address-edit box">
|
||||
<?= \front\view\ShopClient::client_menu( [
|
||||
<?= \front\Views\ShopClient::clientMenu( [
|
||||
'active_page' => 'addresses'
|
||||
] );?>
|
||||
<div class="box-title">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="client-panel client-addresses box">
|
||||
<?= \front\view\ShopClient::client_menu( [
|
||||
<?= \front\Views\ShopClient::clientMenu( [
|
||||
'active_page' => 'addresses'
|
||||
] );?>
|
||||
<div class="box-title">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<? global $lang;?>
|
||||
<div class="client-panel client-orders-history box">
|
||||
<?= \front\view\ShopClient::client_menu( [
|
||||
<?= \front\Views\ShopClient::clientMenu( [
|
||||
'active_page' => 'orders'
|
||||
] );?>
|
||||
<div class="box-title">
|
||||
|
||||
@@ -205,4 +205,262 @@ class CategoryRepositoryTest extends TestCase
|
||||
|
||||
$this->assertSame('Kategoria testowa', $repository->categoryTitle(10));
|
||||
}
|
||||
|
||||
// ===== Frontend methods tests =====
|
||||
|
||||
public function testGetCategorySortReturnsZeroForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame(0, $repository->getCategorySort(0));
|
||||
$this->assertSame(0, $repository->getCategorySort(-1));
|
||||
}
|
||||
|
||||
public function testGetCategorySortReturnsSortType(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')
|
||||
->with('pp_shop_categories', 'sort_type', ['id' => 5])
|
||||
->willReturn('3');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame(3, $repository->getCategorySort(5));
|
||||
}
|
||||
|
||||
public function testCategoryNameReturnsEmptyForInvalidInput(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame('', $repository->categoryName(0, 'pl'));
|
||||
$this->assertSame('', $repository->categoryName(5, ''));
|
||||
}
|
||||
|
||||
public function testCategoryNameReturnsTitle(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')
|
||||
->with('pp_shop_categories_langs', 'title', [
|
||||
'AND' => [
|
||||
'category_id' => 10,
|
||||
'lang_id' => 'pl',
|
||||
],
|
||||
])
|
||||
->willReturn('Elektronika');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame('Elektronika', $repository->categoryName(10, 'pl'));
|
||||
}
|
||||
|
||||
public function testCategoryNameReturnsEmptyWhenNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')->willReturn(null);
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame('', $repository->categoryName(10, 'pl'));
|
||||
}
|
||||
|
||||
public function testFrontCategoryDetailsReturnsEmptyForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame([], $repository->frontCategoryDetails(0, 'pl'));
|
||||
$this->assertSame([], $repository->frontCategoryDetails(-1, 'en'));
|
||||
}
|
||||
|
||||
public function testFrontCategoryDetailsReturnsCategoryWithLanguage(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
|
||||
$callIndex = 0;
|
||||
$mockDb->method('get')
|
||||
->willReturnCallback(function ($table) use (&$callIndex) {
|
||||
$callIndex++;
|
||||
if ($table === 'pp_shop_categories') {
|
||||
return [
|
||||
'id' => 7,
|
||||
'status' => 1,
|
||||
'sort_type' => 2,
|
||||
'parent_id' => null,
|
||||
];
|
||||
}
|
||||
if ($table === 'pp_shop_categories_langs') {
|
||||
return [
|
||||
'category_id' => 7,
|
||||
'lang_id' => 'pl',
|
||||
'title' => 'Odzież',
|
||||
'seo_link' => 'odziez',
|
||||
];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
$result = $repository->frontCategoryDetails(7, 'pl');
|
||||
|
||||
$this->assertSame(7, $result['id']);
|
||||
$this->assertSame('Odzież', $result['language']['title']);
|
||||
$this->assertSame('odziez', $result['language']['seo_link']);
|
||||
}
|
||||
|
||||
public function testFrontCategoryDetailsReturnsEmptyWhenCategoryNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')->willReturn(null);
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame([], $repository->frontCategoryDetails(999, 'pl'));
|
||||
}
|
||||
|
||||
public function testCategoriesTreeReturnsEmptyWhenNoCategories(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('select')->willReturn([]);
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame([], $repository->categoriesTree('pl'));
|
||||
}
|
||||
|
||||
public function testCategoryProductsCountReturnsZeroForInvalidInput(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('query');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame(0, $repository->categoryProductsCount(0, 'pl'));
|
||||
$this->assertSame(0, $repository->categoryProductsCount(5, ''));
|
||||
}
|
||||
|
||||
public function testCategoryProductsCountReturnsCount(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
|
||||
$stmt = $this->createMock(\PDOStatement::class);
|
||||
$stmt->method('fetchAll')->willReturn([[15]]);
|
||||
|
||||
$mockDb->method('query')->willReturn($stmt);
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame(15, $repository->categoryProductsCount(3, 'pl'));
|
||||
}
|
||||
|
||||
public function testProductsIdReturnsEmptyForInvalidInput(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('query');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame([], $repository->productsId(0, 0, 'pl', 12, 0));
|
||||
$this->assertSame([], $repository->productsId(5, 0, '', 12, 0));
|
||||
}
|
||||
|
||||
public function testProductsIdReturnsProductIds(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
|
||||
$stmt = $this->createMock(\PDOStatement::class);
|
||||
$stmt->method('fetchAll')->willReturn([
|
||||
['id' => 101],
|
||||
['id' => 102],
|
||||
['id' => 103],
|
||||
]);
|
||||
|
||||
$mockDb->method('query')->willReturn($stmt);
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
$result = $repository->productsId(5, 1, 'pl', 12, 0);
|
||||
|
||||
$this->assertSame([101, 102, 103], $result);
|
||||
}
|
||||
|
||||
public function testBlogCategoryProductsReturnsEmptyForInvalidInput(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('query');
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
$this->assertSame([], $repository->blogCategoryProducts(0, 'pl', 5));
|
||||
$this->assertSame([], $repository->blogCategoryProducts(5, '', 5));
|
||||
$this->assertSame([], $repository->blogCategoryProducts(5, 'pl', 0));
|
||||
}
|
||||
|
||||
public function testBlogCategoryProductsReturnsIds(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
|
||||
$stmt = $this->createMock(\PDOStatement::class);
|
||||
$stmt->method('fetchAll')->willReturn([
|
||||
['id' => 201],
|
||||
['id' => 202],
|
||||
]);
|
||||
|
||||
$mockDb->method('query')->willReturn($stmt);
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
$result = $repository->blogCategoryProducts(3, 'pl', 5);
|
||||
|
||||
$this->assertSame([201, 202], $result);
|
||||
}
|
||||
|
||||
public function testPaginatedCategoryProductsReturnsEmptyWhenNoProducts(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
|
||||
$stmt = $this->createMock(\PDOStatement::class);
|
||||
$stmt->method('fetchAll')->willReturn([[0]]);
|
||||
|
||||
$mockDb->method('query')->willReturn($stmt);
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
$result = $repository->paginatedCategoryProducts(5, 0, 'pl', 1);
|
||||
|
||||
$this->assertSame([], $result['products']);
|
||||
$this->assertSame(0, $result['ls']);
|
||||
}
|
||||
|
||||
public function testPaginatedCategoryProductsClampsPage(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
|
||||
$countStmt = $this->createMock(\PDOStatement::class);
|
||||
$countStmt->method('fetchAll')->willReturn([[25]]);
|
||||
|
||||
$productsStmt = $this->createMock(\PDOStatement::class);
|
||||
$productsStmt->method('fetchAll')->willReturn([
|
||||
['id' => 301],
|
||||
['id' => 302],
|
||||
]);
|
||||
|
||||
$callIndex = 0;
|
||||
$mockDb->method('query')
|
||||
->willReturnCallback(function () use (&$callIndex, $countStmt, $productsStmt) {
|
||||
$callIndex++;
|
||||
return $callIndex === 1 ? $countStmt : $productsStmt;
|
||||
});
|
||||
|
||||
$repository = new CategoryRepository($mockDb);
|
||||
|
||||
// 25 products / 12 per page = 3 pages; page 99 should clamp to 3
|
||||
$result = $repository->paginatedCategoryProducts(5, 0, 'pl', 99);
|
||||
$this->assertSame(3, $result['ls']);
|
||||
$this->assertSame([301, 302], $result['products']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,4 +131,505 @@ class ClientRepositoryTest extends TestCase
|
||||
$this->assertSame(4, $totals['total_orders']);
|
||||
$this->assertSame(456.78, $totals['total_spent']);
|
||||
}
|
||||
|
||||
// ===== Frontend methods =====
|
||||
|
||||
public function testClientDetailsReturnsNullForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->clientDetails(0));
|
||||
$this->assertNull($repo->clientDetails(-1));
|
||||
}
|
||||
|
||||
public function testClientDetailsReturnsRowOnSuccess(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->with('pp_shop_clients', '*', ['id' => 5])
|
||||
->willReturn(['id' => 5, 'email' => 'jan@example.com']);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->clientDetails(5);
|
||||
$this->assertSame('jan@example.com', $result['email']);
|
||||
}
|
||||
|
||||
public function testClientDetailsReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn(false);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->clientDetails(999));
|
||||
}
|
||||
|
||||
public function testClientEmailReturnsNullForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->clientEmail(0));
|
||||
}
|
||||
|
||||
public function testClientEmailReturnsStringOnSuccess(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->with('pp_shop_clients', 'email', ['id' => 3])
|
||||
->willReturn('test@example.com');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertSame('test@example.com', $repo->clientEmail(3));
|
||||
}
|
||||
|
||||
public function testClientEmailReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn(false);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->clientEmail(999));
|
||||
}
|
||||
|
||||
public function testClientAddressesReturnsEmptyForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('select');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertSame([], $repo->clientAddresses(0));
|
||||
}
|
||||
|
||||
public function testClientAddressesReturnsRows(): void
|
||||
{
|
||||
$rows = [
|
||||
['id' => 1, 'client_id' => 5, 'city' => 'Warszawa'],
|
||||
['id' => 2, 'client_id' => 5, 'city' => 'Kraków'],
|
||||
];
|
||||
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('select')
|
||||
->with('pp_shop_clients_addresses', '*', ['client_id' => 5])
|
||||
->willReturn($rows);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertCount(2, $repo->clientAddresses(5));
|
||||
}
|
||||
|
||||
public function testClientAddressesHandlesFalseFromDb(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('select')
|
||||
->willReturn(false);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertSame([], $repo->clientAddresses(1));
|
||||
}
|
||||
|
||||
public function testAddressDetailsReturnsNullForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->addressDetails(0));
|
||||
}
|
||||
|
||||
public function testAddressDetailsReturnsRow(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->with('pp_shop_clients_addresses', '*', ['id' => 7])
|
||||
->willReturn(['id' => 7, 'city' => 'Gdańsk']);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->addressDetails(7);
|
||||
$this->assertSame('Gdańsk', $result['city']);
|
||||
}
|
||||
|
||||
public function testAddressDeleteReturnsFalseForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('delete');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertFalse($repo->addressDelete(0));
|
||||
}
|
||||
|
||||
public function testAddressDeleteReturnsTrueOnSuccess(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('delete')
|
||||
->with('pp_shop_clients_addresses', ['id' => 3])
|
||||
->willReturn(1);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertTrue($repo->addressDelete(3));
|
||||
}
|
||||
|
||||
public function testAddressSaveReturnsFalseForInvalidClientId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('insert');
|
||||
$mockDb->expects($this->never())->method('update');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertFalse($repo->addressSave(0, null, ['name' => 'Jan']));
|
||||
}
|
||||
|
||||
public function testAddressSaveInsertsNewAddress(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('insert')
|
||||
->with(
|
||||
'pp_shop_clients_addresses',
|
||||
$this->callback(function ($row) {
|
||||
return $row['client_id'] === 5
|
||||
&& $row['name'] === 'Jan'
|
||||
&& $row['city'] === 'Warszawa';
|
||||
})
|
||||
)
|
||||
->willReturn(1);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->addressSave(5, null, [
|
||||
'name' => 'Jan',
|
||||
'surname' => 'Kowalski',
|
||||
'street' => 'Marszałkowska 1',
|
||||
'postal_code' => '00-001',
|
||||
'city' => 'Warszawa',
|
||||
'phone' => '123456789',
|
||||
]);
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testAddressSaveUpdatesExistingAddress(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('update')
|
||||
->with(
|
||||
'pp_shop_clients_addresses',
|
||||
$this->callback(function ($row) {
|
||||
return $row['name'] === 'Anna' && !isset($row['client_id']);
|
||||
}),
|
||||
['AND' => ['client_id' => 5, 'id' => 10]]
|
||||
)
|
||||
->willReturn(1);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->addressSave(5, 10, [
|
||||
'name' => 'Anna',
|
||||
'surname' => 'Nowak',
|
||||
'street' => 'Piłsudskiego 2',
|
||||
'postal_code' => '30-001',
|
||||
'city' => 'Kraków',
|
||||
'phone' => '987654321',
|
||||
]);
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testMarkAddressAsCurrentReturnsFalseForInvalidIds(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('update');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertFalse($repo->markAddressAsCurrent(0, 1));
|
||||
$this->assertFalse($repo->markAddressAsCurrent(1, 0));
|
||||
}
|
||||
|
||||
public function testMarkAddressAsCurrentResetsAndSets(): void
|
||||
{
|
||||
$calls = [];
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->exactly(2))
|
||||
->method('update')
|
||||
->willReturnCallback(function ($table, $data, $where) use (&$calls) {
|
||||
$calls[] = ['data' => $data, 'where' => $where];
|
||||
return 1;
|
||||
});
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertTrue($repo->markAddressAsCurrent(5, 3));
|
||||
|
||||
$this->assertSame(['current' => 0], $calls[0]['data']);
|
||||
$this->assertSame(['client_id' => 5], $calls[0]['where']);
|
||||
$this->assertSame(['current' => 1], $calls[1]['data']);
|
||||
$this->assertSame(['AND' => ['client_id' => 5, 'id' => 3]], $calls[1]['where']);
|
||||
}
|
||||
|
||||
public function testAuthenticateReturnsErrorOnEmptyInput(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
|
||||
$result = $repo->authenticate('', 'pass');
|
||||
$this->assertSame('error', $result['status']);
|
||||
|
||||
$result = $repo->authenticate('jan@example.com', '');
|
||||
$this->assertSame('error', $result['status']);
|
||||
}
|
||||
|
||||
public function testAuthenticateReturnsErrorWhenClientNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn(false);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->authenticate('nobody@example.com', 'pass');
|
||||
$this->assertSame('error', $result['status']);
|
||||
$this->assertSame('logowanie-nieudane', $result['code']);
|
||||
}
|
||||
|
||||
public function testAuthenticateReturnsInactiveForUnconfirmedAccount(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn([
|
||||
'id' => 1,
|
||||
'password' => md5('2026-01-01 00:00:00' . 'test123'),
|
||||
'register_date' => '2026-01-01 00:00:00',
|
||||
'hash' => 'abc123',
|
||||
'status' => 0,
|
||||
]);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->authenticate('jan@example.com', 'test123');
|
||||
$this->assertSame('inactive', $result['status']);
|
||||
$this->assertSame('abc123', $result['hash']);
|
||||
}
|
||||
|
||||
public function testAuthenticateReturnsErrorOnWrongPassword(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn([
|
||||
'id' => 1,
|
||||
'password' => md5('2026-01-01 00:00:00' . 'correct'),
|
||||
'register_date' => '2026-01-01 00:00:00',
|
||||
'hash' => 'abc',
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->authenticate('jan@example.com', 'wrong');
|
||||
$this->assertSame('error', $result['status']);
|
||||
$this->assertSame('logowanie-blad-nieprawidlowe-haslo', $result['code']);
|
||||
}
|
||||
|
||||
public function testAuthenticateReturnsOkOnSuccess(): void
|
||||
{
|
||||
$registerDate = '2026-01-01 00:00:00';
|
||||
$password = 'test123';
|
||||
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
[
|
||||
'id' => 5,
|
||||
'password' => md5($registerDate . $password),
|
||||
'register_date' => $registerDate,
|
||||
'hash' => 'abc',
|
||||
'status' => 1,
|
||||
],
|
||||
['id' => 5, 'email' => 'jan@example.com', 'name' => 'Jan']
|
||||
);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->authenticate('jan@example.com', $password);
|
||||
$this->assertSame('ok', $result['status']);
|
||||
$this->assertSame(5, $result['client']['id']);
|
||||
}
|
||||
|
||||
public function testCreateClientReturnsNullOnEmptyInput(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('count');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->createClient('', 'pass', false));
|
||||
$this->assertNull($repo->createClient('jan@example.com', '', false));
|
||||
}
|
||||
|
||||
public function testCreateClientReturnsNullWhenEmailTaken(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('count')
|
||||
->with('pp_shop_clients', ['email' => 'jan@example.com'])
|
||||
->willReturn(1);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->createClient('jan@example.com', 'pass', false));
|
||||
}
|
||||
|
||||
public function testCreateClientReturnsIdAndHashOnSuccess(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())->method('count')->willReturn(0);
|
||||
$mockDb->expects($this->once())
|
||||
->method('insert')
|
||||
->with(
|
||||
'pp_shop_clients',
|
||||
$this->callback(function ($row) {
|
||||
return $row['email'] === 'jan@example.com'
|
||||
&& $row['agremment_marketing'] === 1
|
||||
&& !empty($row['hash'])
|
||||
&& !empty($row['password']);
|
||||
})
|
||||
)
|
||||
->willReturn(1);
|
||||
$mockDb->expects($this->once())->method('id')->willReturn(42);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->createClient('jan@example.com', 'pass', true);
|
||||
$this->assertSame(42, $result['id']);
|
||||
$this->assertNotEmpty($result['hash']);
|
||||
}
|
||||
|
||||
public function testConfirmRegistrationReturnsNullOnEmptyHash(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->confirmRegistration(''));
|
||||
}
|
||||
|
||||
public function testConfirmRegistrationReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn(false);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->confirmRegistration('nonexistent'));
|
||||
}
|
||||
|
||||
public function testConfirmRegistrationActivatesAndReturnsEmail(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->willReturnOnConsecutiveCalls(10, 'jan@example.com');
|
||||
$mockDb->expects($this->once())
|
||||
->method('update')
|
||||
->with('pp_shop_clients', ['status' => 1], ['id' => 10]);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertSame('jan@example.com', $repo->confirmRegistration('validhash'));
|
||||
}
|
||||
|
||||
public function testGenerateNewPasswordReturnsNullOnEmptyHash(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->generateNewPassword(''));
|
||||
}
|
||||
|
||||
public function testGenerateNewPasswordReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn(false);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->generateNewPassword('badhash'));
|
||||
}
|
||||
|
||||
public function testGenerateNewPasswordReturnsEmailAndPassword(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn([
|
||||
'id' => 5,
|
||||
'email' => 'jan@example.com',
|
||||
'register_date' => '2026-01-01 00:00:00',
|
||||
]);
|
||||
$mockDb->expects($this->once())
|
||||
->method('update')
|
||||
->with(
|
||||
'pp_shop_clients',
|
||||
$this->callback(function ($data) {
|
||||
return $data['password_recovery'] === 0
|
||||
&& !empty($data['password']);
|
||||
}),
|
||||
['id' => 5]
|
||||
);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$result = $repo->generateNewPassword('validhash');
|
||||
$this->assertSame('jan@example.com', $result['email']);
|
||||
$this->assertSame(10, strlen($result['password']));
|
||||
}
|
||||
|
||||
public function testInitiatePasswordRecoveryReturnsNullOnEmptyEmail(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('get');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->initiatePasswordRecovery(''));
|
||||
}
|
||||
|
||||
public function testInitiatePasswordRecoveryReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn(false);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertNull($repo->initiatePasswordRecovery('nobody@example.com'));
|
||||
}
|
||||
|
||||
public function testInitiatePasswordRecoverySetsRecoveryFlagAndReturnsHash(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->once())
|
||||
->method('get')
|
||||
->willReturn('abc123hash');
|
||||
$mockDb->expects($this->once())
|
||||
->method('update')
|
||||
->with('pp_shop_clients', ['password_recovery' => 1], ['email' => 'jan@example.com']);
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertSame('abc123hash', $repo->initiatePasswordRecovery('jan@example.com'));
|
||||
}
|
||||
|
||||
public function testClientOrdersReturnsEmptyForInvalidId(): void
|
||||
{
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->expects($this->never())->method('select');
|
||||
|
||||
$repo = new ClientRepository($mockDb);
|
||||
$this->assertSame([], $repo->clientOrders(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ class Helpers
|
||||
public static function seo($str) { return $str; }
|
||||
public static function delete_dir($path) {}
|
||||
public static function alert($msg) {}
|
||||
public static function error($msg) {}
|
||||
public static function lang($key) { return $key; }
|
||||
public static function delete_session($key) { unset($_SESSION[$key]); }
|
||||
public static function htacces() {}
|
||||
public static function delete_cache() {}
|
||||
public static function get($key) { return null; }
|
||||
|
||||
BIN
updates/0.20/ver_0.289.zip
Normal file
BIN
updates/0.20/ver_0.289.zip
Normal file
Binary file not shown.
5
updates/0.20/ver_0.289_files.txt
Normal file
5
updates/0.20/ver_0.289_files.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
F: ../autoload/front/factory/class.ShopCategory.php
|
||||
F: ../autoload/front/view/class.ShopCategory.php
|
||||
F: ../autoload/front/factory/class.ShopClient.php
|
||||
F: ../autoload/front/view/class.ShopClient.php
|
||||
F: ../autoload/front/controls/class.ShopClient.php
|
||||
@@ -1,3 +1,8 @@
|
||||
<b>ver. 0.289 - 17.02.2026</b><br />
|
||||
- UPDATE - migracja front\factory\ShopCategory + front\view\ShopCategory do Domain\Category\CategoryRepository + front\Views\ShopCategory
|
||||
- UPDATE - migracja front\factory\ShopClient + front\view\ShopClient + front\controls\ShopClient do Domain\Client\ClientRepository + front\Views\ShopClient + front\Controllers\ShopClientController
|
||||
- FIX - usuniety hardcoded password bypass 'Legia1916' w logowaniu klienta
|
||||
<hr>
|
||||
<b>ver. 0.288 - 17.02.2026</b><br />
|
||||
- UPDATE - migracja front\factory\ShopBasket do Domain\Basket\BasketCalculator (4 metody statyczne)
|
||||
- UPDATE - migracja front\controls\ShopBasket do front\Controllers\ShopBasketController (camelCase, instancyjny)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?
|
||||
$current_ver = 288;
|
||||
$current_ver = 289;
|
||||
|
||||
for ($i = 1; $i <= $current_ver; $i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user