From 437d4c78dc1e2f3bdfd8623ea49d4ea20e6270da Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Tue, 17 Feb 2026 09:38:45 +0100 Subject: [PATCH] ver. 0.288: BasketCalculator + ShopBasketController migration, cms\Layout removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- ajax.php | 2 +- autoload/Domain/Basket/BasketCalculator.php | 62 ++++++ autoload/cms/class.Layout.php | 44 ---- .../ShopBasketController.php} | 191 ++++++------------ autoload/front/controls/class.Site.php | 36 ++-- autoload/front/factory/class.ShopBasket.php | 66 ------ autoload/front/factory/class.ShopOrder.php | 2 +- .../front/factory/class.ShopTransport.php | 4 +- autoload/front/view/class.Site.php | 2 +- docs/CHANGELOG.md | 23 ++- docs/FRONTEND_REFACTORING_PLAN.md | 8 +- docs/PROJECT_STRUCTURE.md | 4 +- docs/TESTING.md | 19 +- docs/UPDATE_INSTRUCTIONS.md | 12 +- templates/shop-basket/basket-mini.php | 4 +- .../shop-basket/basket-payments-methods.php | 2 +- templates/shop-basket/summary-view.php | 2 +- .../Domain/Basket/BasketCalculatorTest.php | 67 ++++++ updates/0.20/ver_0.288.zip | Bin 0 -> 17295 bytes updates/0.20/ver_0.288_files.txt | 3 + updates/changelog.php | 7 + updates/versions.php | 2 +- 22 files changed, 287 insertions(+), 275 deletions(-) create mode 100644 autoload/Domain/Basket/BasketCalculator.php delete mode 100644 autoload/cms/class.Layout.php rename autoload/front/{controls/class.ShopBasket.php => Controllers/ShopBasketController.php} (70%) delete mode 100644 autoload/front/factory/class.ShopBasket.php create mode 100644 tests/Unit/Domain/Basket/BasketCalculatorTest.php create mode 100644 updates/0.20/ver_0.288.zip create mode 100644 updates/0.20/ver_0.288_files.txt diff --git a/ajax.php b/ajax.php index 076a823..1cdff48 100644 --- a/ajax.php +++ b/ajax.php @@ -63,7 +63,7 @@ if ( $a == 'basket_change_transport' ) \Shared\Helpers\Helpers::set_session( 'transport_id', \Shared\Helpers\Helpers::get( 'transport_id' ) ); $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' ) ); echo json_encode( [ 'summary' => \Shared\Helpers\Helpers::decimal( $basket_summary + $transport_cost ) . ' zł' ] ); diff --git a/autoload/Domain/Basket/BasketCalculator.php b/autoload/Domain/Basket/BasketCalculator.php new file mode 100644 index 0000000..85082e0 --- /dev/null +++ b/autoload/Domain/Basket/BasketCalculator.php @@ -0,0 +1,62 @@ += 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; + } +} diff --git a/autoload/cms/class.Layout.php b/autoload/cms/class.Layout.php deleted file mode 100644 index c1f872a..0000000 --- a/autoload/cms/class.Layout.php +++ /dev/null @@ -1,44 +0,0 @@ - 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 ); - } -} \ No newline at end of file diff --git a/autoload/front/controls/class.ShopBasket.php b/autoload/front/Controllers/ShopBasketController.php similarity index 70% rename from autoload/front/controls/class.ShopBasket.php rename to autoload/front/Controllers/ShopBasketController.php index c97ba0e..eeea899 100644 --- a/autoload/front/controls/class.ShopBasket.php +++ b/autoload/front/Controllers/ShopBasketController.php @@ -1,22 +1,20 @@ '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' ) ); - echo json_encode( [ - 'result' => 'ok' - ] ); + echo json_encode( [ 'result' => 'ok' ] ); exit; } - public static function basket_remove_product() + public function basketRemoveProduct() { global $lang_id; @@ -31,24 +29,10 @@ class ShopBasket \Shared\Helpers\Helpers::set_session( 'basket', $basket ); - echo json_encode( [ - '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; + $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id ); } - public static function basket_increase_quantity_product() + public function basketIncreaseQuantityProduct() { global $lang_id; @@ -66,25 +50,10 @@ class ShopBasket \Shared\Helpers\Helpers::set_session( 'basket', $basket ); - echo json_encode( [ - '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; + $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id ); } - public static function basket_decrease_quantity_product() + public function basketDecreaseQuantityProduct() { global $lang_id; @@ -102,24 +71,10 @@ class ShopBasket \Shared\Helpers\Helpers::set_session( 'basket', $basket ); - echo json_encode( [ - '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; + $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id ); } - public static function basket_change_quantity_product() + public function basketChangeQuantityProduct() { global $lang_id; @@ -139,24 +94,10 @@ class ShopBasket $basket = \Shared\Helpers\Helpers::get_session( 'basket' ); - echo json_encode( [ - '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; + $this->jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id ); } - static public function product_message_change() + public function productMessageChange() { $basket = \Shared\Helpers\Helpers::get_session( 'basket' ); $basket[ \Shared\Helpers\Helpers::get( 'position_code' ) ]['message'] = \Shared\Helpers\Helpers::get( 'product_message' ); @@ -164,30 +105,29 @@ class ShopBasket exit; } - public static function basket_add_product() + public function basketAddProduct() { $basket = \shop\Basket::validate_basket( \Shared\Helpers\Helpers::get_session( 'basket' ) ); $values_tmp = json_decode( \Shared\Helpers\Helpers::get( 'values' ), true ); + $values = []; + $attributes = []; + $custom_fields = []; foreach( $values_tmp as $key => $val ) $values[ $val['name'] ] = $val['value']; - // sprawdzam pola pod kątem wybranych atrybutów foreach( $values as $key => $val ) { if ( $key != 'product-id' and $key != 'quantity' and $key != 'product-message' and strpos( $key, 'custom_field' ) === false ) $attributes[] = $val; } - // stwórz tablicę dodatkowych pól wyszukując na podstawie custom_field[1], custom_field[2] itd. foreach( $values as $key => $val ) { if ( strpos( $key, 'custom_field' ) !== false ) { - // extract number from custom_field[1], custom_field[2] etc. preg_match( '/\d+/', $key, $matches ); $custom_field_id = $matches[0]; - $custom_fields[ $custom_field_id ] = $val; } } @@ -199,12 +139,10 @@ class ShopBasket $values['attributes'] = $attributes; } - $values['wp'] = \front\factory\ShopProduct::product_wp( $values[ 'product-id' ] ); $attributes_implode = ''; - // generuj unikalny kod produktu dodanego do koszyka - if ( is_array( $attributes ) ) + if ( is_array( $attributes ) and count( $attributes ) > 0 ) $attributes_implode = implode( '|', $attributes ); $product_code = md5( $values['product-id'] . $attributes_implode . $values['product-message'] . json_encode( $custom_fields ) ); @@ -225,95 +163,78 @@ class ShopBasket echo json_encode( [ 'result' => 'ok', - '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 ), + 'basket_mini_count' => \Domain\Basket\BasketCalculator::countProductsText( \Domain\Basket\BasketCalculator::countProducts( $basket ) ), + 'basket_mini_value' => \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ), 'product_sets' => \shop\Product::product_sets_when_add_to_basket( (int)$values['product-id'] ) ] ); exit; } - // sprawdzam czy została wybrana forma wysylki inpost i czy został wybrany paczkomat - static public function transport_method_inpost_check() + public function transportMethodInpostCheck() { - 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' ) ) { - echo json_encode( [ - 'result' => 'bad' - ] ); + echo json_encode( [ 'result' => 'bad' ] ); 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' ) ) { - echo json_encode( [ - 'result' => 'bad' - ] ); + echo json_encode( [ 'result' => 'bad' ] ); exit; } } - echo json_encode( [ - 'result' => 'ok' - ] ); + echo json_encode( [ 'result' => 'ok' ] ); exit; } - // sprawdzam czy został wybrany paczkomat - static public function inpost_check() { + public function inpostCheck() + { if ( !\Shared\Helpers\Helpers::get_session( 'basket-inpost-info' ) ) - echo json_encode( [ - 'result' => 'bad' - ] ); + echo json_encode( [ 'result' => 'bad' ] ); else - echo json_encode( [ - 'result' => 'ok' - ] ); + echo json_encode( [ 'result' => 'ok' ] ); 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_info', \Shared\Helpers\Helpers::get( 'orlen_point_name' ) ); - echo json_encode( [ - 'result' => 'ok' - ] ); + echo json_encode( [ 'result' => 'ok' ] ); exit; } - public static function inpost_save() + public function inpostSave() { \Shared\Helpers\Helpers::set_session( 'basket-inpost-info', \Shared\Helpers\Helpers::get( 'paczkomat' ) ); - echo json_encode( [ - 'result' => 'ok' - ] ); + echo json_encode( [ 'result' => 'ok' ] ); 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' ) ); - echo json_encode( [ - 'result' => 'ok' - ] ); + echo json_encode( [ 'result' => 'ok' ] ); 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' ) ); - echo json_encode( [ - 'result' => 'ok' - ] ); + echo json_encode( [ 'result' => 'ok' ] ); 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' ) ); @@ -327,7 +248,7 @@ class ShopBasket exit; } - public static function summary_view() + public function summaryView() { global $lang_id, $settings; @@ -352,11 +273,9 @@ class ShopBasket ] ); } - // zapisanie koszyka jako zamówienie - static public function basket_save() + public function basketSave() { $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' ) ) ) { @@ -418,7 +337,7 @@ class ShopBasket } } - public static function main_view() + public function mainView() { global $lang_id, $page, $settings; @@ -456,4 +375,22 @@ class ShopBasket ] ); } -} \ No newline at end of file + 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; + } +} diff --git a/autoload/front/controls/class.Site.php b/autoload/front/controls/class.Site.php index e382bbb..6957a7d 100644 --- a/autoload/front/controls/class.Site.php +++ b/autoload/front/controls/class.Site.php @@ -5,30 +5,34 @@ class Site { 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' ) ); - if ( is_array( $results ) ) foreach ( $results as $row ) - $class .= ucfirst( $row ); + $controllerClass = '\front\Controllers\\' . $moduleName . 'Controller'; + if ( class_exists( $controllerClass ) and property_exists( $controllerClass, 'title' ) and isset( $controllerClass::$title[$actionCamel] ) ) + 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' ) ) - return $class::$title[$property]; + return $class::$title[$action]; } static public function title() { 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' ) ); - if ( is_array( $results ) ) foreach ( $results as $row ) - $class .= ucfirst( $row ); + $controllerClass = '\front\Controllers\\' . $moduleName . 'Controller'; + if ( class_exists( $controllerClass ) and property_exists( $controllerClass, 'title' ) and isset( $controllerClass::$title[$actionCamel] ) ) + 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' ) ) - return $class::$title[$property] . ' | ' . $settings['firm_name']; + return $class::$title[$action] . ' | ' . $settings['firm_name']; } public static function route( $product = '', $category = '' ) @@ -64,8 +68,9 @@ class Site if ( isset( $controllerFactories[$moduleName] ) and $action ) { $controller = $controllerFactories[$moduleName](); - if ( method_exists( $controller, $action ) ) - return $controller->$action(); + $actionCamel = lcfirst( implode( '', array_map( 'ucfirst', explode( '_', $action ) ) ) ); + if ( method_exists( $controller, $actionCamel ) ) + return $controller->$actionCamel(); } // stare klasy @@ -162,6 +167,9 @@ class Site new \Domain\Newsletter\NewsletterRepository( $mdb ) ); }, + 'ShopBasket' => function() { + return new \front\Controllers\ShopBasketController(); + }, ]; } } diff --git a/autoload/front/factory/class.ShopBasket.php b/autoload/front/factory/class.ShopBasket.php deleted file mode 100644 index 4dc507b..0000000 --- a/autoload/front/factory/class.ShopBasket.php +++ /dev/null @@ -1,66 +0,0 @@ -= 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; - } - -} \ No newline at end of file diff --git a/autoload/front/factory/class.ShopOrder.php b/autoload/front/factory/class.ShopOrder.php index 39681cf..9414e84 100644 --- a/autoload/front/factory/class.ShopOrder.php +++ b/autoload/front/factory/class.ShopOrder.php @@ -97,7 +97,7 @@ class ShopOrder $transport = \front\factory\ShopTransport::transport( $transport_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_date = date( 'Y-m-d H:i:s' ); $hash = md5( $order_number . time() ); diff --git a/autoload/front/factory/class.ShopTransport.php b/autoload/front/factory/class.ShopTransport.php index 994fb67..6f5b3d4 100644 --- a/autoload/front/factory/class.ShopTransport.php +++ b/autoload/front/factory/class.ShopTransport.php @@ -30,7 +30,7 @@ class ShopTransport $transports_tmp = unserialize( $objectData ); } - $wp_summary = \front\factory\ShopBasket::summary_wp( $basket ); + $wp_summary = \Domain\Basket\BasketCalculator::summaryWp( $basket ); 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++ ){ if($transports[ $i ]['delivery_free'] == 1) { diff --git a/autoload/front/view/class.Site.php b/autoload/front/view/class.Site.php index 113ecc7..7f16e05 100644 --- a/autoload/front/view/class.Site.php +++ b/autoload/front/view/class.Site.php @@ -29,7 +29,7 @@ class Site $scontainersRepo = new \Domain\Scontainers\ScontainersRepository( $GLOBALS['mdb'] ); 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' ) ) $layout = $layoutsRepo->getArticleLayout( (int) \Shared\Helpers\Helpers::get( 'article' ) ); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 602fea1..743800d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -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 - **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 --- -*Dokument aktualizowany: 2026-02-17* +*Dokument aktualizowany: 2026-02-17 (ver. 0.288)* diff --git a/docs/FRONTEND_REFACTORING_PLAN.md b/docs/FRONTEND_REFACTORING_PLAN.md index 0f0e318..f71cdd8 100644 --- a/docs/FRONTEND_REFACTORING_PLAN.md +++ b/docs/FRONTEND_REFACTORING_PLAN.md @@ -14,7 +14,7 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D | Klasa | Status | Logika biznesowa | |-------|--------|-----------------| | 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 | | ShopOrder | KRYTYCZNY | Webhooki płatności (tPay, Przelewy24, Hotpay) — bezpośrednie operacje DB | | 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 | | Articles | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback | | 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 | | ShopPaymentMethod | 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) | 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) 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'` 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` +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') --- diff --git a/docs/PROJECT_STRUCTURE.md b/docs/PROJECT_STRUCTURE.md index 9d3a651..3a03fb0 100644 --- a/docs/PROJECT_STRUCTURE.md +++ b/docs/PROJECT_STRUCTURE.md @@ -108,9 +108,9 @@ shopPRO/ │ │ ├── Helpers/ # Helpers (ex class.S.php) │ │ └── Tpl/ # Tpl (silnik szablonow) │ ├── front/ # Klasy frontendu -│ │ ├── Controllers/ # Nowe kontrolery DI (Newsletter) +│ │ ├── Controllers/ # Nowe kontrolery DI (Newsletter, ShopBasket) │ │ ├── Views/ # Nowe widoki (Newsletter, Articles, Languages, Banners, Menu, Scontainers) -│ │ ├── controls/ # Kontrolery legacy (Site, ShopBasket, ...) +│ │ ├── controls/ # Kontrolery legacy (Site, ...) │ │ ├── view/ # Widoki legacy (Site, ...) │ │ └── factory/ # Fabryki/helpery (fasady) │ └── shop/ # Klasy sklepu diff --git a/docs/TESTING.md b/docs/TESTING.md index b28b5e1..b8de6d0 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -36,7 +36,13 @@ Alternatywnie (Git Bash): Ostatnio zweryfikowano: 2026-02-17 ```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): @@ -130,6 +136,7 @@ tests/ | | |-- Article/ArticleRepositoryTest.php | | |-- Attribute/AttributeRepositoryTest.php | | |-- Banner/BannerRepositoryTest.php +| | |-- Basket/BasketCalculatorTest.php | | |-- Cache/CacheRepositoryTest.php | | |-- Coupon/CouponRepositoryTest.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/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) Ostatnio zweryfikowano: 2026-02-17 diff --git a/docs/UPDATE_INSTRUCTIONS.md b/docs/UPDATE_INSTRUCTIONS.md index 4fe783e..41e1e53 100644 --- a/docs/UPDATE_INSTRUCTIONS.md +++ b/docs/UPDATE_INSTRUCTIONS.md @@ -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.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: - - `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: - - `updates/changelog.php` (dodany wpis `ver. 0.287`) - - `updates/versions.php` (`$current_ver = 287`) + - `updates/changelog.php` (dodany wpis `ver. 0.288`) + - `updates/versions.php` (`$current_ver = 288`) - Weryfikacja testow przed publikacja: - - `OK (476 tests, 1512 assertions)` + - `OK (484 tests, 1528 assertions)` ### 1. Określ numer wersji Sprawdź ostatnią wersję w `updates/` i zwiększ o 1. diff --git a/templates/shop-basket/basket-mini.php b/templates/shop-basket/basket-mini.php index b23395b..8c01a76 100644 --- a/templates/shop-basket/basket-mini.php +++ b/templates/shop-basket/basket-mini.php @@ -7,7 +7,7 @@
Twój koszyk
- : basket );?> + : basket );?> = 5 ): echo 'produktów'; break; } ?>
- : basket, $this -> coupon ) );?> zł + : basket, $this -> coupon ) );?>
diff --git a/templates/shop-basket/basket-payments-methods.php b/templates/shop-basket/basket-payments-methods.php index de3b909..7dd8187 100644 --- a/templates/shop-basket/basket-payments-methods.php +++ b/templates/shop-basket/basket-payments-methods.php @@ -10,7 +10,7 @@ $coupon = \Shared\Helpers\Helpers::get_session( 'coupon' ); $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; ?> payment_methods ) ): foreach ( $this -> payment_methods as $payment_method ):?> = 40 and $basket_summary <= 1000 ):?> diff --git a/templates/shop-basket/summary-view.php b/templates/shop-basket/summary-view.php index fc079a6..5fbd284 100644 --- a/templates/shop-basket/summary-view.php +++ b/templates/shop-basket/summary-view.php @@ -173,7 +173,7 @@ event: "begin_checkout", ecommerce: { currency: "PLN", - value: basket, $this -> coupon ) );?>, + value: basket, $this -> coupon ) );?>, items: [ ] diff --git a/tests/Unit/Domain/Basket/BasketCalculatorTest.php b/tests/Unit/Domain/Basket/BasketCalculatorTest.php new file mode 100644 index 0000000..fba9cde --- /dev/null +++ b/tests/Unit/Domain/Basket/BasketCalculatorTest.php @@ -0,0 +1,67 @@ + 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')); + } +} diff --git a/updates/0.20/ver_0.288.zip b/updates/0.20/ver_0.288.zip new file mode 100644 index 0000000000000000000000000000000000000000..0bbc195e93bd5dd6cb4c4c51b1ba94097bb049eb GIT binary patch literal 17295 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 hAlIL7^*;jwc`498|AYYm0Q2V>0|o%7ver. 0.288 - 17.02.2026
+- 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) +
ver. 0.287 - 17.02.2026
- 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) diff --git a/updates/versions.php b/updates/versions.php index fd8f7d3..a3c5d39 100644 --- a/updates/versions.php +++ b/updates/versions.php @@ -1,5 +1,5 @@