The releases of Shopsys Platform adhere to the Backward Compatibility Promise to make the upgrades to new versions easier and help long-term maintainability.
Since there are 3 possible scenarios for using Shopsys Platform, instructions are divided into these scenarios.
Follow the instructions in relevant sections, e.g. shopsys/coding-standards
or shopsys/http-smoke-testing
.
- upgrade only your composer dependencies and follow the instructions in the guide below
- upgrade locally first - after you fix all issues caused by the upgrade, commit your changes, test your application, and then continue with a deployment onto your server
- upgrade one version at a time:
- start with a working application
- upgrade to the next version
- fix all the issues you encounter
- repeat
- check the instructions in all sections; any of them could be relevant to you
- the typical upgrade sequence should be:
- run
docker-compose down --volumes
to turn off your containers - (macOS only) run
mutagen-compose down --volumes
instead - follow upgrade notes in the Infrastructure section (related to
docker-compose.yml
,Dockerfile
, docker containers,nginx.conf
,php.ini
, etc.) - (MacOS, Windows only) run
docker-sync start
to create volumes - run
docker-compose build --no-cache --pull
to build your images without cache and with the latest version - run
docker-compose up -d --force-recreate --remove-orphans
to start the application again - update the
shopsys/*
dependencies incomposer.json
to the version you are upgrading to- e.g.,
"shopsys/framework": "v7.0.0"
- e.g.,
- follow upgrade notes in the Composer dependencies section (related with
composer.json
) - run
composer update shopsys/* --with-dependencies
- update the
@shopsys/framework
package in yourpackage.json
(in "dependencies" section) to the version you are upgrading to- eg.
"@shopsys/framework": "9.0.4",
- eg.
- run
npm install
to update the NPM dependencies - follow all upgrade notes you have not done yet
- run
php phing clean
- run
php phing db-migrations
to run the database migrations - test your app locally
- commit your changes
- run
composer update
to update the rest of your dependencies, test the app again, and commitcomposer.lock
- run
- if any of the database migrations do not suit you, there is an option to skip it; see our Database Migrations docs
- we may miss something even if we care a lot about these instructions. In case something doesn't work after the upgrade, you'll find more information in the CHANGELOG
- add rounded price value to order process (#2835)
- see #project-base-diff to update your project
- remove link from administrator and customers breadcrumb (#2881)
- see #project-base-diff to update your project
- add test to keep Elasticsearch converter and mapping in sync (#2880)
- see #project-base-diff to update your project
- set products for export to elastic after changing quantity after completing, editing, or deleting order (#2587)
- method
Shopsys\FrameworkBundle\Model\Order\Item\OrderProductFacade::__construct()
changed its interface:public function __construct( protected readonly EntityManagerInterface $em, protected readonly ProductHiddenRecalculator $productHiddenRecalculator, protected readonly ProductSellingDeniedRecalculator $productSellingDeniedRecalculator, protected readonly ProductAvailabilityRecalculationScheduler $productAvailabilityRecalculationScheduler, protected readonly ProductVisibilityFacade $productVisibilityFacade, protected readonly ModuleFacade $moduleFacade, + protected readonly ProductRepository $productRepository, )
- method
- update your project to fix problems with single domain (#2875)
- see #project-base-diff to update your project
- improve product lists in GrapesJS (#2879)
- see #project-base-diff to update your project
- add Symfony Messenger along with RabbitMQ (#2898)
- install application to create new necessary containers (run again
./scripts/install.sh
) – this will overwrite your localdocker-compose.yml
file - see #project-base-diff to update your project
- install application to create new necessary containers (run again
- set the custom logger to the Frontend API (#2882)
- you can set
shopsys.frontend_api.validation_logged_as_error
parameter totrue
to log validation errors with log level ERROR instead of INFO - see #project-base-diff to update your project
- you can set
- start formatting markdown files with Prettier (#2892)
- see #project-base-diff to update your project
- reformat your markdown files by running
php phing standards-fix
in php-fpm container standards(-fix)
targets runs newly addedmarkdown-check/markdown-fix
target, so if you have completely changed thestandards(-fix)
target, remember to add those into yourstandards(-fix)
target
- speed up Product creation in your project (#2903)
- method
Shopsys\FrameworkBundle\Component\Router\FriendlyUrl\FriendlyUrlFacade::resolveUniquenessOfFriendlyUrlAndFlush()
has been renamed toresolveUniquenessOfFriendlyUrl
as it no longer flushes - method
Shopsys\FrameworkBundle\Model\Product\Pricing\ProductManualInputPriceFacade::refresh()
has changed its visibility toprotected
- method
Shopsys\FrameworkBundle\Model\Product\ProductFacade::refreshProductManualInputPrices()
has been moved toShopsys\FrameworkBundle\Model\Product\Pricing\ProductManualInputPriceFacade::refreshProductManualInputPrices()
and changed its visibility topublic
- method
Shopsys\FrameworkBundle\Model\Product\Pricing\ProductManualInputPriceFacade::__construct()
changed its interface:
public function __construct( protected readonly EntityManagerInterface $em, protected readonly ProductManualInputPriceRepository $productManualInputPriceRepository, protected readonly ProductManualInputPriceFactoryInterface $productManualInputPriceFactory, + protected readonly PricingGroupRepository $pricingGroupRepository, )
- method
Shopsys\ProductFeed\GoogleBundle\Model\Product\GoogleProductDomainFacade::saveGoogleProductDomain()
changed its visibility toprotected
- method
Shopsys\ProductFeed\HeurekaBundle\Model\Product\HeurekaProductDomainFacade::saveHeurekaProductDomain()
changed its visibility toprotected
- method
Shopsys\ProductFeed\HeurekaBundle\Model\Product\HeurekaProductDomainFacade::saveHeurekaProductDomain()
changed its visibility toprotected
- method
Shopsys\ProductFeed\ZboziBundle\Model\Product\ZboziProductDomainFacade::saveZboziProductDomain()
changed its visibility toprotected
- see #project-base-diff to update your project
- method
- add consumers and RabbitMQ to deployed application (#2904)
- set new environment variables
RABBITMQ_DEFAULT_USER
,RABBITMQ_DEFAULT_PASS
,RABBITMQ_IP_WHITELIST
in your deployment tool (with use of the default config it will be Gitlab CI) - see #project-base-diff to update your project
- set new environment variables
- re-enable phing target cron (#2875)
- see #project-base-diff to update your project
- prepare core for dispatch/consume system (#2907)
- your custom classes that utilize internal array caching or need to be reset between message consumption should now implement the
\Symfony\Contracts\Service\ResetInterface
interface - method
Shopsys\FrameworkBundle\Model\Product\Pricing\ProductPriceRecalculationScheduler::cleanScheduleForImmediateRecalculation()
has been renamed toreset()
- see #project-base-diff to update your project
- your custom classes that utilize internal array caching or need to be reset between message consumption should now implement the
- replace custom application bootstrapping with symfony/runtime (#2914)
- see #project-base-diff to update your project
- prevent duplicate color parameters in data fixtures (#2911)
- see #project-base-diff to update your project
- enable crons to be run at specified times the same as crons ()
- replace
HourlyFeedCronModule
andDailyFeedCronModule
withFeedCronModule
inconfig/services/cron.yaml
and set it to be run every time crons are run to ensure that all feeds are generated FeedExportCreationDataQueue
has changed, first parameter is now an array ofShopsys\FrameworkBundle\Model\Feed\FeedModule
instances instead of module names- method
Shopsys\FrameworkBundle\Model\Feed\FeedFacade::__construct()
changed its interface:
public function __construct( protected readonly FeedRegistry $feedRegistry, protected readonly ProductVisibilityFacade $productVisibilityFacade, protected readonly FeedExportFactory $feedExportFactory, protected readonly FeedPathProvider $feedPathProvider, protected readonly FilesystemOperator $filesystem, + protected readonly FeedModuleRepository $feedModuleRepository, + protected readonly EntityManagerInterface $em, )
- method
Shopsys\FrameworkBundle\Model\Feed\FeedFacade::getFeedsInfo()
changed its interface:
- public function getFeedsInfo(?string $feedType = null): array + public function getFeedsInfo(bool $onlyForCurrentTime = false): array
- method
Shopsys\FrameworkBundle\Model\Feed\FeedFacade::getFeedsNames()
changed its interface:
- public function getFeedNames(?string $feedType = null): array + public function getFeedNames(bool $onlyForCurrentTime = false): array
- method
Shopsys\FrameworkBundle\Model\Feed\FeedRegistry::__construct()
changed its interface:
public function __construct( - protected readonly array $knownTypes, - protected readonly string $defaultType, + protected readonly FeedRegistry $feedRegistry, + protected readonly ProductVisibilityFacade $productVisibilityFacade, + protected readonly FeedExportFactory $feedExportFactory, + protected readonly FeedPathProvider $feedPathProvider, + protected readonly FilesystemOperator $filesystem, + protected readonly FeedModuleRepository $feedModuleRepository, + protected readonly EntityManagerInterface $em, )
- method
Shopsys\FrameworkBundle\Model\Feed\FeedRegistry::registerFeed()
changed its interface:
- public function registerFeed(FeedInterface $feed, ?string $type = null): void + public function registerFeed(FeedInterface $feed, string $timeHours, string $timeMinutes, array $domainIds): void
- property
Shopsys\FrameworkBundle\Model\Feed\FeedRegistry::$feedsByType
has been replaced with new$feedConfigsByName
property instead - method
Shopsys\FrameworkBundle\Controller\Admin\FeedController::__construct
changed its interface:
public function __construct( protected readonly FeedFacade $feedFacade, protected readonly GridFactory $gridFactory, protected readonly Domain $domain, + protected readonly FeedRegistry $feedRegistry, + protected readonly FeedModuleRepository $feedModuleRepository, )
- method
Shopsys\FrameworkBundle\Model\Feed\Exception\FeedNotFoundException__construct
changed its interface:
public function __construct( string $name, + ?int $domainId = null, ?Exception $previous = null )
- method
Shopsys\FrameworkBundle\Model\Feed\FeedRegistry::getFeeds()
has been replaced with methodShopsys\FrameworkBundle\Model\Feed\FeedRegistry::getFeedsForCurrentTime()
- method
Shopsys\FrameworkBundle\Model\Feed\FeedRegistry::getAllFeeds()
has been replaced with methodShopsys\FrameworkBundle\Model\Feed\FeedRegistry::getAllFeedConfigs()
- method
Shopsys\FrameworkBundle\Model\Feed\FeedRegistry::getFeedByName()
has been replaced with methodShopsys\FrameworkBundle\Model\Feed\FeedRegistry::getFeedConfigByName()
- method
Shopsys\FrameworkBundle\Model\Feed\FeedRegistry::assertTypeIsKnown()
has been removed without a replacement - see #project-base-diff to update your project
- replace
-
add rounded price value to order process (#2835)
- see #project-base-diff to update your project
-
remove unnecessary default value for domain config in zustand (#2888)
- you probably do not need the default value as well, as we set it right at the beginning of page load
- see #project-base-diff to update your project
-
fixed undefined window error (#2882)
- in one of the previous PR's, the
canUseDom
function was removed, but it caused the application to fail in some cases - because of that, the function was brought back as a constant (as
isClient
) and some checks were reinstantiated - in your code, you should pay attention to where you are using the window object and make sure it is available (either by checking the logic or by explicitly wrapping it in a check)
- also keep in mind that
canUseDom
is nowisClient
andisServer
is now used as!isClient
- for not-discussed changes, see #project-base-diff to update your project
- in one of the previous PR's, the
-
add Prettier plugin and ESlint plugins and rules (#2874)
- see #project-base-diff to update your project
- to check if your repo follows the new rules run
pnpm run check
orpnpm run check--fix
to autofix in Storefront folder or in Storefront Docker container - Prettier
- added
@trivago/prettier-plugin-sort-imports
plugin
- added
- ESlint
- added
eslint-plugin-no-relative-import-paths
plugin and rules enforcing rules for imports - new rules
- no-helpers-are-exported-from-component-file
- react/jsx-no-useless-fragment
- react/self-closing-comp
- react/jsx-sort-props
- no-relative-import-paths/no-relative-import-paths
- added
-
add Related Products tab on product detail page (#2885)
- see #project-base-diff to update your project
-
improve product lists in GrapesJS (#2879)
- see #project-base-diff to update your project
-
add instant page skeleton after link click (#2863)
- before page must first load and then skeleton was shown, now we pass page type to
ExtendedLink
component which allow us to display immediately after user click on the link proper skeleton for the required page - some reorganization and renaming was done to Skeletons, we basically have only two types of skeletons, for pages and modules, since it is sometimes difficult to recognise which one is which, we have added Page word, but this was not perfect in folder organization, that's why it's been added word Module as well, to organize skeletons better way
- added missing skeletons for Homepage and Stores
- adjustments to current skeletons to match the design of a page better
- see #project-base-diff to update your project
- before page must first load and then skeleton was shown, now we pass page type to
-
refactoring of various error-related matters on SF (#2871)
- the goal was to shine light on some of the not-well-understood places in regard of error handling on SF
- for you to get the most out of this PR, you should check
error-handling.md
in SF docs, which is a direct result of this PR - it contains explanations and tips on how to improve error handling in your SF code as well
- for not-discussed changes, see #project-base-diff to update your project
- see #project-base-diff to update your project
-
refactor
ProductVariantsTable
(#2899)ProductVariantsTable
component was made with table element but on smaller screens it was styled more like list. This was causing styling difficulties. That's why it has been replaced with grid with combination of flexbox.- components
ProductVariantsTableRow
andVariant
were removed - component
ProductVariantsTable
was renamed toProductDetailVariantsTable
so it matches parent folder where it's placed - see #project-base-diff to update your project
-
add equal spacing to the Category page (#2900)
- see #project-base-diff to update your project
-
auth (loading) improvements (#2897)
-
window.location.href
assignments were replaced withrouter
operations because of unexpected behavior, where the current URL (thus also properties of the router) changed even before the transition, which caused useEffects and some other conditions to re-run and potentially fails -
if you manipulate
window.location
in your code, you should remove it and userouter
instead -
loading states for all auth operations (login, logout, registration) are now implemented, so if you have some custom states, you should also handle these
-
optimistic display of skeletons was implemented for
logout-loading
and bothregistration-loading...
states- this was possible as we know that the user is going to be redirected to homepage
- however, since we do not know the page type of the redirect destination after login, we do not display any skeleton there
- if you always know the destination of redirect after login, chances are you can improve the UX by implementing the following changes:
// inside useAuth.tsx login updateAuthLoadingState( loginResult.data.Login.showCartMergeInfo ? 'login-loading-with-cart-modifications' : 'login-loading', ); // add this line to start showing the skeleton before the redirect updatePageLoadingState({ isPageLoading: true, redirectPageType: 'my-page-type', }); if (rewriteUrl) { router.replace(rewriteUrl).then(() => router.reload()); } else { router.reload(); }
-
-
added USPs on product detail page (#2887)
- see #project-base-diff to update your project
-
fix sizes of product actions buttons (#2896)
- now we have unified sizes of add to cart buttons
- see #project-base-diff to update your project
-
fix Comparison for not logged in users (#2905)
- unified code for Comparison and Wishlist
- refactored Zustand store to use only one store (User Store) for all cartUuid, wishlistUuid and comparisonUuid
-
search on search page is now not called if search query is empty (#2895)
- see #project-base-diff to update your project
-
fix set default delivery address country (#2902)
- see #project-base-diff to update your project
-
fix router server access error on PageGuard (#2909)
- see #project-base-diff to update your project
-
fix Cart list unit text (#2910)
-
remove
Heading
component (#2894)- it was decided by FE team that this component is not beneficial
- it was replaced with general H tags and styles were put into globals.css, styles were also included with new classes (
h1
,h2
,h3
,h4
) which we can use to style text which suppose to look like heading but it is not important enough for mark with H tag - also from those headings and heading classes was remove margin bottom since spacing should be set in exact place where this component is used, not everywhere
- see #project-base-diff to update your project
-
remove error logging for when gtmSafePush is called outside of the client (#2920)
- logging this type of error brought no business value, thus it was removed
- if you want to treat this ignorable event in a more strict way, you might want to keep the logging, but then you have to improve its behavior yourself
- see #project-base-diff to update your project
-
added logic for ordering GTM events (#2921)
- added a GTM context provider for synchronizing events
- if you have any logic for syncing your events, you can move it to this provider
- docs were added, so you can base your changes on those
- see #project-base-diff to update your project
-
improve SEO categories logic in regards to non-SEO-sensitive filters (#2891)
- we moved some config from various files to
config/constants
and warmly suggest you do the same, as it improves the app and testing - we also moved some hooks from
helpers
tohooks
and suggest you do the same with all hooks, as it again improves the app and testing - the implemented functionality for dynamic switching between SEO-sensitivity for various filters is only implemented on SF, so applying these changes won't make it work on BE
- if you do not need SEO categories, these changes might be irrelevant altogether
- flag and brand values are now merged after change of default parameters (leaving SEO category) instead of overwritting. This is a bug fix and you should apply it to your changes as well.
- see #project-base-diff to update your project
- we moved some config from various files to
-
category data fetching logic improvements (#2893)
- these changes primarily focus on fixing loading and fetching logic on the category page
- there were certain bugs with double loads, skeleton glitches, etc. but they were mostly caused by the added complexity of SEO categories, so if you do not have those and your fetching logic is thus much simpler, you probably do not need most of these changes
- the category detail fetching was also rewritten to the generated URQL hook, which can be beneficial for you if your logic allows you to do so
- one thing that you should definitely consider is removal of
onRouteChangeError
from the page loading logic, as the previous implementation was rather invalid. See commit message for more details
-
added additional skeletons, sync store across browser tabs, use BroadcastChannel to refresh cart after Add/Remove to/from cart, ExtendedNextLink was refactored, remove EmptyCartWrapper (#2906)
-
added skeletons for Wishlist, Comparison and My orders, Order detail and Product Main Variant page
-
fix Add to cart/wishlist/comparison feature when using multiple tabs
- Before this change if I open multiple tabs as fresh unlogged user without
cartUuid
, then in each tab I add product to cart, I am experiencing that each tab contains different products in cart, even after refresh. Same applies toWishlist
andComparison
. This is solved now by addingBroadcastChannel
middleware to Zustand store, so now it uses always only first returneduuid
. - removed redirect to homepage after logout, this was not good approach
removeItemFromCartAction
inuseRemoveProductFromCart
is nowvoid
type (doesn't returnCartFragmentApi
) and in case of error is returned from API it refect cart so user gets latest cart updates- Zustand package is upgraded to latest version
- We are now manually hydrating the Zustand store during the initial page load. This means that we no longer need to resolve mismatches between the UI rendered by the server and the UI managed on the client side. Code from this example may look familiar to you, this is no longer needed:
const [isFetchingPaused, setIsFetchingPaused] = useState(true); const [{ data: comparisonData, fetching }] = useComparisonQueryApi({ variables: { comparisonUuid }, pause: isFetchingPaused, pause: !comparisonUuid && !isUserLoggedIn, }); useEffect(() => { setIsFetchingPaused(!comparisonUuid && !isUserLoggedIn); }, [comparisonUuid]);
- Before this change if I open multiple tabs as fresh unlogged user without
-
fix Add to cart/wishlist/comparison feature when using multiple tabs
- As fresh unlogged user without
cartUuid
I open multiple tabs, then in each tab I add product to cart, I am experiencing that each tab contains different products in cart, even after refresh. Same applies toWishlist
andComparison
. This was solved by addingBroadcastChannel
middleware to Zustand store, so now it uses always only first returneduuid
. - Added useBroadcastChannel hook, this allow us to use Broadcast Channel for better handling multitab behavior. Together with the first type Auth for handling Authentication. Now when user log in/out, other tabs are reloaded.
- As fresh unlogged user without
-
ExtendedNextLink was refactored
- remove static type from your links
- if you have custom types, we moved it from STATIC_PAGES to CUSTOM_PAGES
-
use BroadcastChannel to refresh cart
- whereever you use modifications for cart there should be also implemented the same behavior
- Removed isCartEmpty value from useCurrentCart. This was causing unnecessary checks for cart value after check if isCartEmpty, because Typescript was not able to recognise that cart value is already checked. For the same reason some handling cases were more difficult to write and understand. Also check for cart exist (!cartUuid && !isUserLoggedIn) was needed basically everywhere where cart values were using, this was added to this hook and now cart value is consistent
-
remove EmptyCartWrapper
- this component was making whole order process very difficult to predict behavior, now we have the logic from this component splitted into each page of the order process which makes it much more predictable
- also this fixes bug with infinite cart page loading
-