ver. 0.288: BasketCalculator + ShopBasketController migration, cms\Layout removal

- Migrate front\factory\ShopBasket → Domain\Basket\BasketCalculator (4 static methods, 18 callers updated)
- Migrate front\controls\ShopBasket → front\Controllers\ShopBasketController (camelCase, instance methods)
- Add snake_case→camelCase action dispatch for new controllers in Site::route()
- Update title()/page_title() to check front\Controllers\ before fallback
- Remove cms\Layout class (replaced by $layoutsRepo->find())
- Add 8 tests for BasketCalculator (484 tests, 1528 assertions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 09:38:45 +01:00
parent 3b50ba7990
commit 437d4c78dc
22 changed files with 287 additions and 275 deletions

View File

@@ -63,7 +63,7 @@ if ( $a == 'basket_change_transport' )
\Shared\Helpers\Helpers::set_session( 'transport_id', \Shared\Helpers\Helpers::get( 'transport_id' ) ); \Shared\Helpers\Helpers::set_session( 'transport_id', \Shared\Helpers\Helpers::get( 'transport_id' ) );
$basket = \Shared\Helpers\Helpers::get_session( 'basket' ); $basket = \Shared\Helpers\Helpers::get_session( 'basket' );
$basket_summary = \front\factory\ShopBasket::summary_price( $basket, null ); $basket_summary = \Domain\Basket\BasketCalculator::summaryPrice( $basket, null );
$transport_cost = \front\factory\ShopTransport::transport_cost( \Shared\Helpers\Helpers::get( 'transport_id' ) ); $transport_cost = \front\factory\ShopTransport::transport_cost( \Shared\Helpers\Helpers::get( 'transport_id' ) );
echo json_encode( [ 'summary' => \Shared\Helpers\Helpers::decimal( $basket_summary + $transport_cost ) . ' zł' ] ); echo json_encode( [ 'summary' => \Shared\Helpers\Helpers::decimal( $basket_summary + $transport_cost ) . ' zł' ] );

View File

@@ -0,0 +1,62 @@
<?php
namespace Domain\Basket;
class BasketCalculator
{
public static function summaryWp($basket)
{
$wp = 0;
if (is_array($basket)) {
foreach ($basket as $product) {
$wp += $product['wp'] * $product['quantity'];
}
}
return $wp;
}
public static function countProductsText($count)
{
$count = (int)$count;
if ($count === 1) {
return $count . ' produkt';
}
if ($count >= 2 && $count <= 4) {
return $count . ' produkty';
}
return $count . ' produktów';
}
public static function summaryPrice($basket, $coupon = null)
{
global $lang_id;
$summary = 0;
if (is_array($basket)) {
foreach ($basket as $position) {
$product = \shop\Product::getFromCache((int)$position['product-id'], $lang_id);
$product_price_tmp = \shop\Product::calculate_basket_product_price(
(float)$product['price_brutto_promo'],
(float)$product['price_brutto'],
$coupon,
$position
);
$summary += $product_price_tmp['price_new'] * $position['quantity'];
}
}
return \Shared\Helpers\Helpers::normalize_decimal($summary);
}
public static function countProducts($basket)
{
$count = 0;
if (is_array($basket)) {
foreach ($basket as $product) {
$count += $product['quantity'];
}
}
return $count;
}
}

View File

@@ -1,44 +0,0 @@
<?
namespace cms;
class Layout implements \ArrayAccess
{
public function __construct( int $layout_id )
{
global $mdb;
$result = $mdb -> get( 'pp_layouts', '*', [ 'id' => $layout_id ] );
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
$this -> $key = $val;
}
public function __get( $variable )
{
if ( array_key_exists( $variable, $this -> data ) )
return $this -> $variable;
}
public function __set( $variable, $value )
{
$this -> $variable = $value;
}
public function offsetExists( $offset )
{
return isset( $this -> $offset );
}
public function offsetGet( $offset )
{
return $this -> $offset;
}
public function offsetSet( $offset, $value )
{
$this -> $offset = $value;
}
public function offsetUnset( $offset )
{
unset( $this -> $offset );
}
}

View File

@@ -1,22 +1,20 @@
<?php <?php
namespace front\controls; namespace front\Controllers;
class ShopBasket class ShopBasketController
{ {
public static $title = [ public static $title = [
'main_view' => 'Koszyk' 'mainView' => 'Koszyk'
]; ];
public static function basket_message_save() public function basketMessageSave()
{ {
\Shared\Helpers\Helpers::set_session( 'basket_message', \Shared\Helpers\Helpers::get( 'basket_message' ) ); \Shared\Helpers\Helpers::set_session( 'basket_message', \Shared\Helpers\Helpers::get( 'basket_message' ) );
echo json_encode( [ echo json_encode( [ 'result' => 'ok' ] );
'result' => 'ok'
] );
exit; exit;
} }
public static function basket_remove_product() public function basketRemoveProduct()
{ {
global $lang_id; global $lang_id;
@@ -31,24 +29,10 @@ class ShopBasket
\Shared\Helpers\Helpers::set_session( 'basket', $basket ); \Shared\Helpers\Helpers::set_session( 'basket', $basket );
echo json_encode( [ $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id );
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
'basket' => $basket,
'lang_id' => $lang_id,
'coupon' => $coupon
] ),
'basket_mini_count' => \front\factory\ShopBasket::count_products_text( \front\factory\ShopBasket::count_products( $basket ) ),
'basket_mini_value' => \front\factory\ShopBasket::summary_price( $basket, $coupon ),
'products_count' => count( $basket ),
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
'transports_methods' => \front\factory\ShopTransport::transport_methods( $basket, $coupon ),
'transport_id' => $basket_transport_method_id
] )
] );
exit;
} }
public static function basket_increase_quantity_product() public function basketIncreaseQuantityProduct()
{ {
global $lang_id; global $lang_id;
@@ -66,25 +50,10 @@ class ShopBasket
\Shared\Helpers\Helpers::set_session( 'basket', $basket ); \Shared\Helpers\Helpers::set_session( 'basket', $basket );
echo json_encode( [ $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id );
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
'basket' => $basket,
'lang_id' => $lang_id,
'coupon' => $coupon
] ),
'basket_mini_count' => \front\factory\ShopBasket::count_products_text( \front\factory\ShopBasket::count_products( $basket ) ),
'basket_mini_value' => \front\factory\ShopBasket::summary_price( $basket, $coupon ),
'products_count' => count( $basket ),
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
'transports_methods' => \front\factory\ShopTransport::transport_methods( $basket, $coupon ),
'transport_id' => $basket_transport_method_id
] )
]
);
exit;
} }
public static function basket_decrease_quantity_product() public function basketDecreaseQuantityProduct()
{ {
global $lang_id; global $lang_id;
@@ -102,24 +71,10 @@ class ShopBasket
\Shared\Helpers\Helpers::set_session( 'basket', $basket ); \Shared\Helpers\Helpers::set_session( 'basket', $basket );
echo json_encode( [ $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id );
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
'basket' => $basket,
'lang_id' => $lang_id,
'coupon' => $coupon
] ),
'basket_mini_count' => \front\factory\ShopBasket::count_products_text( \front\factory\ShopBasket::count_products( $basket ) ),
'basket_mini_value' => \front\factory\ShopBasket::summary_price( $basket, $coupon ),
'products_count' => count( $basket ),
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
'transports_methods' => \front\factory\ShopTransport::transport_methods( $basket, $coupon ),
'transport_id' => $basket_transport_method_id
] )
] );
exit;
} }
public static function basket_change_quantity_product() public function basketChangeQuantityProduct()
{ {
global $lang_id; global $lang_id;
@@ -139,24 +94,10 @@ class ShopBasket
$basket = \Shared\Helpers\Helpers::get_session( 'basket' ); $basket = \Shared\Helpers\Helpers::get_session( 'basket' );
echo json_encode( [ $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id );
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
'basket' => $basket,
'lang_id' => $lang_id,
'coupon' => $coupon
] ),
'basket_mini_count' => \front\factory\ShopBasket::count_products_text( \front\factory\ShopBasket::count_products( $basket ) ),
'basket_mini_value' => \front\factory\ShopBasket::summary_price( $basket, $coupon ),
'products_count' => count( $basket ),
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
'transports_methods' => \front\factory\ShopTransport::transport_methods( $basket, $coupon ),
'transport_id' => $basket_transport_method_id
] )
] );
exit;
} }
static public function product_message_change() public function productMessageChange()
{ {
$basket = \Shared\Helpers\Helpers::get_session( 'basket' ); $basket = \Shared\Helpers\Helpers::get_session( 'basket' );
$basket[ \Shared\Helpers\Helpers::get( 'position_code' ) ]['message'] = \Shared\Helpers\Helpers::get( 'product_message' ); $basket[ \Shared\Helpers\Helpers::get( 'position_code' ) ]['message'] = \Shared\Helpers\Helpers::get( 'product_message' );
@@ -164,30 +105,29 @@ class ShopBasket
exit; exit;
} }
public static function basket_add_product() public function basketAddProduct()
{ {
$basket = \shop\Basket::validate_basket( \Shared\Helpers\Helpers::get_session( 'basket' ) ); $basket = \shop\Basket::validate_basket( \Shared\Helpers\Helpers::get_session( 'basket' ) );
$values_tmp = json_decode( \Shared\Helpers\Helpers::get( 'values' ), true ); $values_tmp = json_decode( \Shared\Helpers\Helpers::get( 'values' ), true );
$values = [];
$attributes = [];
$custom_fields = [];
foreach( $values_tmp as $key => $val ) foreach( $values_tmp as $key => $val )
$values[ $val['name'] ] = $val['value']; $values[ $val['name'] ] = $val['value'];
// sprawdzam pola pod kątem wybranych atrybutów
foreach( $values as $key => $val ) foreach( $values as $key => $val )
{ {
if ( $key != 'product-id' and $key != 'quantity' and $key != 'product-message' and strpos( $key, 'custom_field' ) === false ) if ( $key != 'product-id' and $key != 'quantity' and $key != 'product-message' and strpos( $key, 'custom_field' ) === false )
$attributes[] = $val; $attributes[] = $val;
} }
// stwórz tablicę dodatkowych pól wyszukując na podstawie custom_field[1], custom_field[2] itd.
foreach( $values as $key => $val ) foreach( $values as $key => $val )
{ {
if ( strpos( $key, 'custom_field' ) !== false ) if ( strpos( $key, 'custom_field' ) !== false )
{ {
// extract number from custom_field[1], custom_field[2] etc.
preg_match( '/\d+/', $key, $matches ); preg_match( '/\d+/', $key, $matches );
$custom_field_id = $matches[0]; $custom_field_id = $matches[0];
$custom_fields[ $custom_field_id ] = $val; $custom_fields[ $custom_field_id ] = $val;
} }
} }
@@ -199,12 +139,10 @@ class ShopBasket
$values['attributes'] = $attributes; $values['attributes'] = $attributes;
} }
$values['wp'] = \front\factory\ShopProduct::product_wp( $values[ 'product-id' ] ); $values['wp'] = \front\factory\ShopProduct::product_wp( $values[ 'product-id' ] );
$attributes_implode = ''; $attributes_implode = '';
// generuj unikalny kod produktu dodanego do koszyka if ( is_array( $attributes ) and count( $attributes ) > 0 )
if ( is_array( $attributes ) )
$attributes_implode = implode( '|', $attributes ); $attributes_implode = implode( '|', $attributes );
$product_code = md5( $values['product-id'] . $attributes_implode . $values['product-message'] . json_encode( $custom_fields ) ); $product_code = md5( $values['product-id'] . $attributes_implode . $values['product-message'] . json_encode( $custom_fields ) );
@@ -225,95 +163,78 @@ class ShopBasket
echo json_encode( [ echo json_encode( [
'result' => 'ok', 'result' => 'ok',
'basket_mini_count' => \front\factory\ShopBasket::count_products_text( \front\factory\ShopBasket::count_products( $basket ) ), 'basket_mini_count' => \Domain\Basket\BasketCalculator::countProductsText( \Domain\Basket\BasketCalculator::countProducts( $basket ) ),
'basket_mini_value' => \front\factory\ShopBasket::summary_price( $basket, $coupon ), 'basket_mini_value' => \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ),
'product_sets' => \shop\Product::product_sets_when_add_to_basket( (int)$values['product-id'] ) 'product_sets' => \shop\Product::product_sets_when_add_to_basket( (int)$values['product-id'] )
] ); ] );
exit; exit;
} }
// sprawdzam czy została wybrana forma wysylki inpost i czy został wybrany paczkomat public function transportMethodInpostCheck()
static public function transport_method_inpost_check()
{ {
if ( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) === '2' or \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) === '1' ) $transport_id = \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' );
if ( $transport_id === '2' or $transport_id === '1' )
{ {
if ( !\Shared\Helpers\Helpers::get_session( 'basket-inpost-info' ) ) if ( !\Shared\Helpers\Helpers::get_session( 'basket-inpost-info' ) )
{ {
echo json_encode( [ echo json_encode( [ 'result' => 'bad' ] );
'result' => 'bad'
] );
exit; exit;
} }
} }
if ( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) === '9' ) if ( $transport_id === '9' )
{ {
if ( !\Shared\Helpers\Helpers::get_session( 'basket_orlen_point_id' ) ) if ( !\Shared\Helpers\Helpers::get_session( 'basket_orlen_point_id' ) )
{ {
echo json_encode( [ echo json_encode( [ 'result' => 'bad' ] );
'result' => 'bad'
] );
exit; exit;
} }
} }
echo json_encode( [ echo json_encode( [ 'result' => 'ok' ] );
'result' => 'ok'
] );
exit; exit;
} }
// sprawdzam czy został wybrany paczkomat public function inpostCheck()
static public function inpost_check() { {
if ( !\Shared\Helpers\Helpers::get_session( 'basket-inpost-info' ) ) if ( !\Shared\Helpers\Helpers::get_session( 'basket-inpost-info' ) )
echo json_encode( [ echo json_encode( [ 'result' => 'bad' ] );
'result' => 'bad'
] );
else else
echo json_encode( [ echo json_encode( [ 'result' => 'ok' ] );
'result' => 'ok'
] );
exit; exit;
} }
static public function orlen_save() public function orlenSave()
{ {
\Shared\Helpers\Helpers::set_session( 'basket_orlen_point_id', \Shared\Helpers\Helpers::get( 'orlen_point_id' ) ); \Shared\Helpers\Helpers::set_session( 'basket_orlen_point_id', \Shared\Helpers\Helpers::get( 'orlen_point_id' ) );
\Shared\Helpers\Helpers::set_session( 'basket_orlen_point_info', \Shared\Helpers\Helpers::get( 'orlen_point_name' ) ); \Shared\Helpers\Helpers::set_session( 'basket_orlen_point_info', \Shared\Helpers\Helpers::get( 'orlen_point_name' ) );
echo json_encode( [ echo json_encode( [ 'result' => 'ok' ] );
'result' => 'ok'
] );
exit; exit;
} }
public static function inpost_save() public function inpostSave()
{ {
\Shared\Helpers\Helpers::set_session( 'basket-inpost-info', \Shared\Helpers\Helpers::get( 'paczkomat' ) ); \Shared\Helpers\Helpers::set_session( 'basket-inpost-info', \Shared\Helpers\Helpers::get( 'paczkomat' ) );
echo json_encode( [ echo json_encode( [ 'result' => 'ok' ] );
'result' => 'ok'
] );
exit; exit;
} }
public static function basket_payment_method_set() public function basketPaymentMethodSet()
{ {
\Shared\Helpers\Helpers::set_session( 'basket-payment-method-id', \Shared\Helpers\Helpers::get( 'payment_method_id' ) ); \Shared\Helpers\Helpers::set_session( 'basket-payment-method-id', \Shared\Helpers\Helpers::get( 'payment_method_id' ) );
echo json_encode( [ echo json_encode( [ 'result' => 'ok' ] );
'result' => 'ok'
] );
exit; exit;
} }
public static function basket_transport_method_set() public function basketTransportMethodSet()
{ {
\Shared\Helpers\Helpers::set_session( 'basket-transport-method-id', \Shared\Helpers\Helpers::get( 'transport_method_id' ) ); \Shared\Helpers\Helpers::set_session( 'basket-transport-method-id', \Shared\Helpers\Helpers::get( 'transport_method_id' ) );
echo json_encode( [ echo json_encode( [ 'result' => 'ok' ] );
'result' => 'ok'
] );
exit; exit;
} }
public static function basket_payments_methods() public function basketPaymentsMethods()
{ {
\Shared\Helpers\Helpers::set_session( 'basket-transport-method-id', \Shared\Helpers\Helpers::get( 'transport_method_id' ) ); \Shared\Helpers\Helpers::set_session( 'basket-transport-method-id', \Shared\Helpers\Helpers::get( 'transport_method_id' ) );
@@ -327,7 +248,7 @@ class ShopBasket
exit; exit;
} }
public static function summary_view() public function summaryView()
{ {
global $lang_id, $settings; global $lang_id, $settings;
@@ -352,11 +273,9 @@ class ShopBasket
] ); ] );
} }
// zapisanie koszyka jako zamówienie public function basketSave()
static public function basket_save()
{ {
$client = \Shared\Helpers\Helpers::get_session( 'client' ); $client = \Shared\Helpers\Helpers::get_session( 'client' );
$payment_method = \Shared\Helpers\Helpers::get_session( 'basket-payment-method-id' );
if ( \shop\Basket::check_product_quantity_in_stock( \Shared\Helpers\Helpers::get_session( 'basket' ) ) ) if ( \shop\Basket::check_product_quantity_in_stock( \Shared\Helpers\Helpers::get_session( 'basket' ) ) )
{ {
@@ -418,7 +337,7 @@ class ShopBasket
} }
} }
public static function main_view() public function mainView()
{ {
global $lang_id, $page, $settings; global $lang_id, $page, $settings;
@@ -456,4 +375,22 @@ class ShopBasket
] ); ] );
} }
private function jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id )
{
echo json_encode( [
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
'basket' => $basket,
'lang_id' => $lang_id,
'coupon' => $coupon
] ),
'basket_mini_count' => \Domain\Basket\BasketCalculator::countProductsText( \Domain\Basket\BasketCalculator::countProducts( $basket ) ),
'basket_mini_value' => \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ),
'products_count' => count( $basket ),
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
'transports_methods' => \front\factory\ShopTransport::transport_methods( $basket, $coupon ),
'transport_id' => $basket_transport_method_id
] )
] );
exit;
}
} }

View File

@@ -5,30 +5,34 @@ class Site
{ {
static public function page_title() static public function page_title()
{ {
$class = '\front\controls\\'; $moduleName = implode( '', array_map( 'ucfirst', explode( '_', \Shared\Helpers\Helpers::get( 'module' ) ) ) );
$action = \Shared\Helpers\Helpers::get( 'action' );
$actionCamel = lcfirst( implode( '', array_map( 'ucfirst', explode( '_', $action ) ) ) );
$results = explode( '_', \Shared\Helpers\Helpers::get( 'module' ) ); $controllerClass = '\front\Controllers\\' . $moduleName . 'Controller';
if ( is_array( $results ) ) foreach ( $results as $row ) if ( class_exists( $controllerClass ) and property_exists( $controllerClass, 'title' ) and isset( $controllerClass::$title[$actionCamel] ) )
$class .= ucfirst( $row ); return $controllerClass::$title[$actionCamel];
$property = \Shared\Helpers\Helpers::get( 'action' ); $class = '\front\controls\\' . $moduleName;
if ( class_exists( $class ) and property_exists( new $class, 'page_title' ) ) if ( class_exists( $class ) and property_exists( new $class, 'page_title' ) )
return $class::$title[$property]; return $class::$title[$action];
} }
static public function title() static public function title()
{ {
global $settings; global $settings;
$class = '\front\controls\\'; $moduleName = implode( '', array_map( 'ucfirst', explode( '_', \Shared\Helpers\Helpers::get( 'module' ) ) ) );
$action = \Shared\Helpers\Helpers::get( 'action' );
$actionCamel = lcfirst( implode( '', array_map( 'ucfirst', explode( '_', $action ) ) ) );
$results = explode( '_', \Shared\Helpers\Helpers::get( 'module' ) ); $controllerClass = '\front\Controllers\\' . $moduleName . 'Controller';
if ( is_array( $results ) ) foreach ( $results as $row ) if ( class_exists( $controllerClass ) and property_exists( $controllerClass, 'title' ) and isset( $controllerClass::$title[$actionCamel] ) )
$class .= ucfirst( $row ); return $controllerClass::$title[$actionCamel] . ' | ' . $settings['firm_name'];
$property = \Shared\Helpers\Helpers::get( 'action' ); $class = '\front\controls\\' . $moduleName;
if ( class_exists( $class ) and property_exists( new $class, 'title' ) ) if ( class_exists( $class ) and property_exists( new $class, 'title' ) )
return $class::$title[$property] . ' | ' . $settings['firm_name']; return $class::$title[$action] . ' | ' . $settings['firm_name'];
} }
public static function route( $product = '', $category = '' ) public static function route( $product = '', $category = '' )
@@ -64,8 +68,9 @@ class Site
if ( isset( $controllerFactories[$moduleName] ) and $action ) if ( isset( $controllerFactories[$moduleName] ) and $action )
{ {
$controller = $controllerFactories[$moduleName](); $controller = $controllerFactories[$moduleName]();
if ( method_exists( $controller, $action ) ) $actionCamel = lcfirst( implode( '', array_map( 'ucfirst', explode( '_', $action ) ) ) );
return $controller->$action(); if ( method_exists( $controller, $actionCamel ) )
return $controller->$actionCamel();
} }
// stare klasy // stare klasy
@@ -162,6 +167,9 @@ class Site
new \Domain\Newsletter\NewsletterRepository( $mdb ) new \Domain\Newsletter\NewsletterRepository( $mdb )
); );
}, },
'ShopBasket' => function() {
return new \front\Controllers\ShopBasketController();
},
]; ];
} }
} }

View File

@@ -1,66 +0,0 @@
<?php
namespace front\factory;
class ShopBasket
{
public static function summary_wp( $basket )
{
global $mdb;
foreach ( $basket as $product )
{
$wp += $product[ 'wp' ] * $product[ 'quantity' ];
}
return $wp;
}
public static function count_products_text( $count )
{
$count_products = $count;
switch ( true )
{
case ( $count == 0 ): $count_products .= ' produktów';
break;
case ( $count == 1 ): $count_products .= ' produkt';
break;
case ( $count == 2 or $count == 3 or $count == 4 ): $count_products .= ' produkty';
break;
case ( $count >= 5 ): $count_products .= ' produktów';
break;
}
return $count_products;
}
public static function summary_price( $basket, $coupon = null )
{
global $lang_id;
$summary = 0;
if ( is_array( $basket ) )
{
foreach ( $basket as $position )
{
$product = \shop\Product::getFromCache( (int)$position['product-id'], $lang_id );
$product_price_tmp = \shop\Product::calculate_basket_product_price( (float)$product['price_brutto_promo'], (float)$product['price_brutto'], $coupon, $position );
$summary += $product_price_tmp['price_new'] * $position[ 'quantity' ];
}
}
return \Shared\Helpers\Helpers::normalize_decimal( $summary );
}
public static function count_products( $basket )
{
$count = 0;
if ( is_array( $basket ) )
foreach ( $basket as $product )
$count += $product[ 'quantity' ];
return $count;
}
}

View File

@@ -97,7 +97,7 @@ class ShopOrder
$transport = \front\factory\ShopTransport::transport( $transport_id ); $transport = \front\factory\ShopTransport::transport( $transport_id );
$payment_method = \front\factory\ShopPaymentMethod::payment_method( $payment_id ); $payment_method = \front\factory\ShopPaymentMethod::payment_method( $payment_id );
$basket_summary = \front\factory\ShopBasket::summary_price( $basket, $coupon ); $basket_summary = \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon );
$order_number = self::generate_order_number(); $order_number = self::generate_order_number();
$order_date = date( 'Y-m-d H:i:s' ); $order_date = date( 'Y-m-d H:i:s' );
$hash = md5( $order_number . time() ); $hash = md5( $order_number . time() );

View File

@@ -30,7 +30,7 @@ class ShopTransport
$transports_tmp = unserialize( $objectData ); $transports_tmp = unserialize( $objectData );
} }
$wp_summary = \front\factory\ShopBasket::summary_wp( $basket ); $wp_summary = \Domain\Basket\BasketCalculator::summaryWp( $basket );
foreach ( $transports_tmp as $tr ) foreach ( $transports_tmp as $tr )
{ {
@@ -41,7 +41,7 @@ class ShopTransport
} }
if ( \Shared\Helpers\Helpers::normalize_decimal( \front\factory\ShopBasket::summary_price( $basket, $coupon ) ) >= \Shared\Helpers\Helpers::normalize_decimal( $settings['free_delivery'] ) ) if ( \Shared\Helpers\Helpers::normalize_decimal( \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ) ) >= \Shared\Helpers\Helpers::normalize_decimal( $settings['free_delivery'] ) )
{ {
for ( $i = 0; $i < count( $transports ); $i++ ){ for ( $i = 0; $i < count( $transports ); $i++ ){
if($transports[ $i ]['delivery_free'] == 1) { if($transports[ $i ]['delivery_free'] == 1) {

View File

@@ -29,7 +29,7 @@ class Site
$scontainersRepo = new \Domain\Scontainers\ScontainersRepository( $GLOBALS['mdb'] ); $scontainersRepo = new \Domain\Scontainers\ScontainersRepository( $GLOBALS['mdb'] );
if ( (int) \Shared\Helpers\Helpers::get( 'layout_id' ) ) if ( (int) \Shared\Helpers\Helpers::get( 'layout_id' ) )
$layout = new \cms\Layout( (int) \Shared\Helpers\Helpers::get( 'layout_id' ) ); $layout = $layoutsRepo->find( (int) \Shared\Helpers\Helpers::get( 'layout_id' ) );
if ( \Shared\Helpers\Helpers::get( 'article' ) ) if ( \Shared\Helpers\Helpers::get( 'article' ) )
$layout = $layoutsRepo->getArticleLayout( (int) \Shared\Helpers\Helpers::get( 'article' ) ); $layout = $layoutsRepo->getArticleLayout( (int) \Shared\Helpers\Helpers::get( 'article' ) );

View File

@@ -4,6 +4,27 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
--- ---
## ver. 0.288 (2026-02-17) - BasketCalculator + ShopBasketController + cms\Layout removal
- **ShopBasket (factory → Domain)** — migracja na Domain
- NOWY: `Domain\Basket\BasketCalculator` — 4 statyczne metody (`summaryWp`, `countProductsText`, `summaryPrice`, `countProducts`)
- USUNIETA: `front\factory\class.ShopBasket.php` — logika przeniesiona do `BasketCalculator`
- UPDATE: 18 callerow w 7 plikach przepietych na `\Domain\Basket\BasketCalculator::`
- **ShopBasket (controls → Controllers)** — migracja kontrolera
- NOWY: `front\Controllers\ShopBasketController` — instancyjny kontroler z camelCase metodami
- Wyekstrahowano `jsonBasketResponse()` — wspolna odpowiedz JSON dla 4 metod koszyka
- Zainicjalizowano zmienne `$values`, `$attributes`, `$custom_fields` w `basketAddProduct()`
- USUNIETA: `front\controls\class.ShopBasket.php` — zastapiona przez `ShopBasketController`
- UPDATE: `front\controls\Site::route()` — konwersja `snake_case → camelCase` w dispatch dla nowych kontrolerow
- UPDATE: `front\controls\Site::title()` / `page_title()` — sprawdzanie `front\Controllers\*Controller` przed fallback
- Zarejestrowany `'ShopBasket'` w `getControllerFactories()`
- **cms\Layout removal**
- USUNIETA: `autoload/cms/class.Layout.php` — caly folder `autoload/cms/` (1 klasa, bug w `__get()`)
- UPDATE: `front\view\Site::show()``new \cms\Layout(...)``$layoutsRepo->find(...)`
- Testy: 484 OK, 1528 asercji (+8 testow: BasketCalculatorTest)
---
## ver. 0.287 (2026-02-17) - Scontainers + ShopAttribute frontend migration ## ver. 0.287 (2026-02-17) - Scontainers + ShopAttribute frontend migration
- **Scontainers (frontend)** — migracja na Domain - **Scontainers (frontend)** — migracja na Domain
@@ -697,4 +718,4 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
- Metoda `clear_product_cache()` w klasie S - Metoda `clear_product_cache()` w klasie S
--- ---
*Dokument aktualizowany: 2026-02-17* *Dokument aktualizowany: 2026-02-17 (ver. 0.288)*

View File

@@ -14,7 +14,7 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
| Klasa | Status | Logika biznesowa | | Klasa | Status | Logika biznesowa |
|-------|--------|-----------------| |-------|--------|-----------------|
| Site | Router główny | route(), check_url_params(), title() | | Site | Router główny | route(), check_url_params(), title() |
| ShopBasket | MIXED | Operacje koszyka, add/remove/quantity, checkout | | ShopBasket | ZMIGROWANY do `front\Controllers\ShopBasketController` | Operacje koszyka, add/remove/quantity, checkout |
| ShopClient | Fasada | Deleguje do factory | | ShopClient | Fasada | Deleguje do factory |
| ShopOrder | KRYTYCZNY | Webhooki płatności (tPay, Przelewy24, Hotpay) — bezpośrednie operacje DB | | ShopOrder | KRYTYCZNY | Webhooki płatności (tPay, Przelewy24, Hotpay) — bezpośrednie operacje DB |
| ShopProduct | Fasada | lazy_loading, warehouse_message, draw_product_attributes | | ShopProduct | Fasada | lazy_loading, warehouse_message, draw_product_attributes |
@@ -31,7 +31,7 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
| ShopCategory | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback | | ShopCategory | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
| Articles | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback | | Articles | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
| ShopPromotion | ORYGINALNA LOGIKA | WYSOKI — silnik promocji (5 typów) | | ShopPromotion | ORYGINALNA LOGIKA | WYSOKI — silnik promocji (5 typów) |
| ShopBasket | Fasada | ŚREDNI — summary_price, count | | ShopBasket | ZMIGROWANA do `Domain\Basket\BasketCalculator` — usunięta | — |
| ShopTransport | CZĘŚCIOWO zmigrowana | ŚREDNI — transport_methods z filtrowaniem | | ShopTransport | CZĘŚCIOWO zmigrowana | ŚREDNI — transport_methods z filtrowaniem |
| ShopPaymentMethod | ZMIGROWANA (Domain) | — | | ShopPaymentMethod | ZMIGROWANA (Domain) | — |
| ShopStatuses | ZMIGROWANA (Domain) | — | | ShopStatuses | ZMIGROWANA (Domain) | — |
@@ -95,7 +95,7 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
### cms/ (1 klasa) ### cms/ (1 klasa)
| Klasa | Status | | Klasa | Status |
|-------|--------| |-------|--------|
| Layout | BUG w __get() — referuje $this->data które nie istnieje | | Layout | USUNIETA — zastapiona przez `$layoutsRepo->find()` |
### templates/ (75 plików w 15 modułach) ### templates/ (75 plików w 15 modułach)
articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), shop-basket(9), shop-category(6), shop-client(8), shop-coupon(1), shop-order(3), shop-producer(3), shop-product(12), shop-search(3), site(11) articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), shop-basket(9), shop-category(6), shop-client(8), shop-coupon(1), shop-order(3), shop-producer(3), shop-product(12), shop-search(3), site(11)
@@ -114,7 +114,7 @@ articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), sho
1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'` 1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`
2. `front\factory\Settings::get_single_settings_value()` — ignoruje `$param`, zawsze zwraca `firm_name` 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()` 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` 4. ~~`cms\Layout::__get()` — referuje nieistniejące `$this->data`~~ **NAPRAWIONE** — klasa usunięta, zastąpiona przez `$layoutsRepo->find()`
5. `shop\Search` — typo w use: `shop\Produt` (brak 'c') 5. `shop\Search` — typo w use: `shop\Produt` (brak 'c')
--- ---

View File

@@ -108,9 +108,9 @@ shopPRO/
│ │ ├── Helpers/ # Helpers (ex class.S.php) │ │ ├── Helpers/ # Helpers (ex class.S.php)
│ │ └── Tpl/ # Tpl (silnik szablonow) │ │ └── Tpl/ # Tpl (silnik szablonow)
│ ├── front/ # Klasy frontendu │ ├── front/ # Klasy frontendu
│ │ ├── Controllers/ # Nowe kontrolery DI (Newsletter) │ │ ├── Controllers/ # Nowe kontrolery DI (Newsletter, ShopBasket)
│ │ ├── Views/ # Nowe widoki (Newsletter, Articles, Languages, Banners, Menu, Scontainers) │ │ ├── Views/ # Nowe widoki (Newsletter, Articles, Languages, Banners, Menu, Scontainers)
│ │ ├── controls/ # Kontrolery legacy (Site, ShopBasket, ...) │ │ ├── controls/ # Kontrolery legacy (Site, ...)
│ │ ├── view/ # Widoki legacy (Site, ...) │ │ ├── view/ # Widoki legacy (Site, ...)
│ │ └── factory/ # Fabryki/helpery (fasady) │ │ └── factory/ # Fabryki/helpery (fasady)
│ └── shop/ # Klasy sklepu │ └── shop/ # Klasy sklepu

View File

@@ -36,7 +36,13 @@ Alternatywnie (Git Bash):
Ostatnio zweryfikowano: 2026-02-17 Ostatnio zweryfikowano: 2026-02-17
```text ```text
OK (476 tests, 1512 assertions) OK (484 tests, 1528 assertions)
```
Aktualizacja po migracji BasketCalculator + ShopBasketController + cms\Layout removal (2026-02-17, ver. 0.288):
```text
Pelny suite: OK (484 tests, 1528 assertions)
Nowe testy: BasketCalculatorTest (+8: summaryWp, countProducts, countProductsText — singular/plural/cast)
``` ```
Aktualizacja po migracji Scontainers + ShopAttribute frontend (2026-02-17, ver. 0.287): Aktualizacja po migracji Scontainers + ShopAttribute frontend (2026-02-17, ver. 0.287):
@@ -130,6 +136,7 @@ tests/
| | |-- Article/ArticleRepositoryTest.php | | |-- Article/ArticleRepositoryTest.php
| | |-- Attribute/AttributeRepositoryTest.php | | |-- Attribute/AttributeRepositoryTest.php
| | |-- Banner/BannerRepositoryTest.php | | |-- Banner/BannerRepositoryTest.php
| | |-- Basket/BasketCalculatorTest.php
| | |-- Cache/CacheRepositoryTest.php | | |-- Cache/CacheRepositoryTest.php
| | |-- Coupon/CouponRepositoryTest.php | | |-- Coupon/CouponRepositoryTest.php
| | |-- Category/CategoryRepositoryTest.php | | |-- Category/CategoryRepositoryTest.php
@@ -531,6 +538,16 @@ Nowe testy dodane 2026-02-17:
- `tests/Unit/Domain/Layouts/LayoutsRepositoryTest.php` (rozszerzenie: +8 testow frontend: categoryDefaultLayoutId, getDefaultLayout, getProductLayout, getArticleLayout, getCategoryLayout, getActiveLayout) - `tests/Unit/Domain/Layouts/LayoutsRepositoryTest.php` (rozszerzenie: +8 testow frontend: categoryDefaultLayoutId, getDefaultLayout, getProductLayout, getArticleLayout, getCategoryLayout, getActiveLayout)
- `tests/Unit/Domain/Pages/PagesRepositoryTest.php` (rozszerzenie: +8 testow frontend: frontPageDetails, frontMainPageId, frontPageSort, frontLangUrl, frontMenuDetails, frontMenuPages) - `tests/Unit/Domain/Pages/PagesRepositoryTest.php` (rozszerzenie: +8 testow frontend: frontPageDetails, frontMainPageId, frontPageSort, frontLangUrl, frontMenuDetails, frontMenuPages)
## Aktualizacja suite (BasketCalculator + ShopBasketController, ver. 0.288)
Ostatnio zweryfikowano: 2026-02-17
```text
OK (484 tests, 1528 assertions)
```
Nowe testy dodane 2026-02-17:
- `tests/Unit/Domain/Basket/BasketCalculatorTest.php` (8 testow: summaryWp, summaryWpEmpty, countProducts, countProductsEmpty, countProductsTextSingular, countProductsTextPlural2to4, countProductsTextPlural5Plus, countProductsTextCastsToInt)
## Aktualizacja suite (Scontainers + ShopAttribute frontend, ver. 0.287) ## Aktualizacja suite (Scontainers + ShopAttribute frontend, ver. 0.287)
Ostatnio zweryfikowano: 2026-02-17 Ostatnio zweryfikowano: 2026-02-17

View File

@@ -18,16 +18,16 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią
## Procedura tworzenia nowej aktualizacji ## Procedura tworzenia nowej aktualizacji
## Status biezacej aktualizacji (ver. 0.287) ## Status biezacej aktualizacji (ver. 0.288)
- Wersja udostepniona: `0.287` (data: 2026-02-17). - Wersja udostepniona: `0.288` (data: 2026-02-17).
- Pliki publikacyjne: - Pliki publikacyjne:
- `updates/0.20/ver_0.287.zip`, `ver_0.287_files.txt` - `updates/0.20/ver_0.288.zip`, `ver_0.288_files.txt`
- Pliki metadanych aktualizacji: - Pliki metadanych aktualizacji:
- `updates/changelog.php` (dodany wpis `ver. 0.287`) - `updates/changelog.php` (dodany wpis `ver. 0.288`)
- `updates/versions.php` (`$current_ver = 287`) - `updates/versions.php` (`$current_ver = 288`)
- Weryfikacja testow przed publikacja: - Weryfikacja testow przed publikacja:
- `OK (476 tests, 1512 assertions)` - `OK (484 tests, 1528 assertions)`
### 1. Określ numer wersji ### 1. Określ numer wersji
Sprawdź ostatnią wersję w `updates/` i zwiększ o 1. Sprawdź ostatnią wersję w `updates/` i zwiększ o 1.

View File

@@ -7,7 +7,7 @@
<div class="basket-details"> <div class="basket-details">
<div class="title">Twój koszyk</div> <div class="title">Twój koszyk</div>
<div class="details"> <div class="details">
<b><?= ucfirst( $lang['ilosc'] );?></b>: <span id="products-count"><?= $count = \front\factory\ShopBasket::count_products( $this -> basket );?> <b><?= ucfirst( $lang['ilosc'] );?></b>: <span id="products-count"><?= $count = \Domain\Basket\BasketCalculator::countProducts( $this -> basket );?>
<? <?
switch ( true ) switch ( true )
{ {
@@ -17,7 +17,7 @@
case ( $count >= 5 ): echo 'produktów'; break; case ( $count >= 5 ): echo 'produktów'; break;
} }
?></span><br /> ?></span><br />
<b><?= ucfirst( $lang['suma'] );?></b>: <span id="basket-value"><?= \Shared\Helpers\Helpers::decimal( \front\factory\ShopBasket::summary_price( $this -> basket, $this -> coupon ) );?></span> zł <b><?= ucfirst( $lang['suma'] );?></b>: <span id="basket-value"><?= \Shared\Helpers\Helpers::decimal( \Domain\Basket\BasketCalculator::summaryPrice( $this -> basket, $this -> coupon ) );?></span> zł
</div> </div>
</div> </div>
</a> </a>

View File

@@ -10,7 +10,7 @@
$coupon = \Shared\Helpers\Helpers::get_session( 'coupon' ); $coupon = \Shared\Helpers\Helpers::get_session( 'coupon' );
$transport_cost = \front\factory\ShopTransport::transport_cost( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) ); $transport_cost = \front\factory\ShopTransport::transport_cost( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) );
$basket_summary = \front\factory\ShopBasket::summary_price( $basket, $coupon ) + $transport_cost; $basket_summary = \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ) + $transport_cost;
?> ?>
<? if ( is_array( $this -> payment_methods ) ): foreach ( $this -> payment_methods as $payment_method ):?> <? if ( is_array( $this -> payment_methods ) ): foreach ( $this -> payment_methods as $payment_method ):?>
<? if ( $payment_method['id'] != 6 or $payment_method['id'] == 6 and $basket_summary >= 40 and $basket_summary <= 1000 ):?> <? if ( $payment_method['id'] != 6 or $payment_method['id'] == 6 and $basket_summary >= 40 and $basket_summary <= 1000 ):?>

View File

@@ -173,7 +173,7 @@
event: "begin_checkout", event: "begin_checkout",
ecommerce: { ecommerce: {
currency: "PLN", currency: "PLN",
value: <?= \Shared\Helpers\Helpers::normalize_decimal( \front\factory\ShopBasket::summary_price( $this -> basket, $this -> coupon ) );?>, value: <?= \Shared\Helpers\Helpers::normalize_decimal( \Domain\Basket\BasketCalculator::summaryPrice( $this -> basket, $this -> coupon ) );?>,
items: [ items: [
<?= $begin_checkout_items;?> <?= $begin_checkout_items;?>
] ]

View File

@@ -0,0 +1,67 @@
<?php
namespace Tests\Unit\Domain\Basket;
use PHPUnit\Framework\TestCase;
use Domain\Basket\BasketCalculator;
class BasketCalculatorTest extends TestCase
{
public function testSummaryWpCalculatesTotal(): void
{
$basket = [
['wp' => 2, 'quantity' => 3],
['wp' => 5, 'quantity' => 1],
];
$this->assertSame(11, BasketCalculator::summaryWp($basket));
}
public function testSummaryWpReturnsZeroForEmptyBasket(): void
{
$this->assertSame(0, BasketCalculator::summaryWp([]));
$this->assertSame(0, BasketCalculator::summaryWp(null));
}
public function testCountProductsSumsQuantities(): void
{
$basket = [
['quantity' => 2],
['quantity' => 3],
['quantity' => 1],
];
$this->assertSame(6, BasketCalculator::countProducts($basket));
}
public function testCountProductsReturnsZeroForEmptyBasket(): void
{
$this->assertSame(0, BasketCalculator::countProducts([]));
$this->assertSame(0, BasketCalculator::countProducts(null));
}
public function testCountProductsTextSingular(): void
{
$this->assertSame('1 produkt', BasketCalculator::countProductsText(1));
}
public function testCountProductsTextPlural2to4(): void
{
$this->assertSame('2 produkty', BasketCalculator::countProductsText(2));
$this->assertSame('3 produkty', BasketCalculator::countProductsText(3));
$this->assertSame('4 produkty', BasketCalculator::countProductsText(4));
}
public function testCountProductsTextPlural5Plus(): void
{
$this->assertSame('0 produktów', BasketCalculator::countProductsText(0));
$this->assertSame('5 produktów', BasketCalculator::countProductsText(5));
$this->assertSame('12 produktów', BasketCalculator::countProductsText(12));
$this->assertSame('100 produktów', BasketCalculator::countProductsText(100));
}
public function testCountProductsTextCastsToInt(): void
{
$this->assertSame('3 produkty', BasketCalculator::countProductsText('3'));
$this->assertSame('0 produktów', BasketCalculator::countProductsText('abc'));
}
}

BIN
updates/0.20/ver_0.288.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,3 @@
F: ../autoload/cms/class.Layout.php
F: ../autoload/front/controls/class.ShopBasket.php
F: ../autoload/front/factory/class.ShopBasket.php

View File

@@ -1,3 +1,10 @@
<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)
- UPDATE - routing snake_case->camelCase w dispatch dla nowych kontrolerow
- CLEANUP - usunieta klasa cms\Layout (zastapiona $layoutsRepo->find)
- CLEANUP - usuniete 3 klasy legacy (front\factory\ShopBasket, front\controls\ShopBasket, cms\Layout)
<hr>
<b>ver. 0.287 - 17.02.2026</b><br /> <b>ver. 0.287 - 17.02.2026</b><br />
- UPDATE - migracja front\factory\Scontainers do Domain\Scontainers\ScontainersRepository (frontScontainerDetails z Redis cache) - UPDATE - migracja front\factory\Scontainers do Domain\Scontainers\ScontainersRepository (frontScontainerDetails z Redis cache)
- UPDATE - migracja front\factory\ShopAttribute do Domain\Attribute\AttributeRepository (frontAttributeDetails, frontValueDetails z Redis cache) - UPDATE - migracja front\factory\ShopAttribute do Domain\Attribute\AttributeRepository (frontAttributeDetails, frontValueDetails z Redis cache)

View File

@@ -1,5 +1,5 @@
<? <?
$current_ver = 287; $current_ver = 288;
for ($i = 1; $i <= $current_ver; $i++) for ($i = 1; $i <= $current_ver; $i++)
{ {