LastProductList
extends BaseComponent
in package
uses
InitTrait
Lists the newest products for home pages and promo blocks outside the eshop.
The eshop counterpart of LastAdvertList.
Products can otherwise be listed only from ProductSetPresenter, so the home page layout could
not reach them.
Usage:
{control frontEshopLastProductList} {* 4 products in a grid *}
{control frontEshopLastProductList:grid, 8} {* the same with a custom count *}
{control frontEshopLastProductList:deal} {* deal of the week: 1 product with a sale tag *}
{control frontEshopLastProductList:news, 4} {* newest products with a "new" tag *}
{control frontEshopLastProductList:recommended, 4} {* newest products with a "recommended" tag *}
The component name is resolved to the class automatically (ComponentTrait::createStandardComponent()), nothing is registered in DI by hand.
IMPORTANT: product visibility uses the shared
ProductSetEligibilityFilters, the same conditions as
the /eshop list, not an own filter set. Own filters would drift from the list on the first
rule change and an unpublished or deactivated product would leak onto the home page.
Table of Contents
Constants
- LIST_INCLUDES : mixed = ['translations', 'productState', 'tags.translat...
- List includes; keep identical to `ProductSetListTrait::LIST_INCLUDES`.
- SETTINGS_MODULE_ESHOP : int = 4
- Settings module id of the eshop.
Properties
- $container : Container
- $routerDispatcher : RouterDispatcher
- $translator : ITranslator
- $appContext : AppContext
- $componentParameters : array<string, mixed>
- $currencyContext : CurrencyContext|null
- $deviceDetector : DeviceDetector|null
- $languageContext : LanguageContext|null
- $localizationContext : LocalizationContext|null
- $settingContext : SettingContext
- $themeContext : ThemeContext|null
- $translatorDomain : string|null
- IMPORTANT: no translator domain. The product card (`Templates/ProductSet/Items/_item.latte`) translates with full keys (`front.eshop.availability.soldOut`, ...) because the presenter renders it too; with a domain the key would be prefixed twice and the card would print the bare `front.eshop.cart.addToCart` instead of the text.
- $cart : CartManager
- $productSetManager : ProductSetManager
Methods
- __call() : mixed
- Dispatches `renderXyz()` to `render('Xyz', ...$args)`.
- __construct() : mixed
- createComponent() : IComponent|null
- getAppContext() : AppContext
- getComponentParameter() : mixed
- Returns a component parameter, or null when it is not set.
- getContainer() : Container
- getCurrencyContext() : CurrencyContext|null
- getLanguageContext() : LanguageContext|null
- getLocalizationContext() : LocalizationContext|null
- getRouterDispatcher() : RouterDispatcher
- getSettingContext() : SettingContext
- getSettings() : array<int, array<string, mixed>>
- getThemeContext() : ThemeContext|null
- handleAddToCart() : void
- Handles "add to cart" from the product card.
- hasServiceByType() : bool
- Returns whether the container has a service of the given type.
- initializeAllContexts() : void
- injectDeviceDetector() : void
- onCreate() : void
- render() : void
- renderDeal() : void
- Renders the deal of the week: one product with a sale-flag tag (`ProductTag::$saleFlag`).
- renderNews() : void
- Renders the grid of the newest products with a "new" tag (`ProductTag::$newFlag`); without any such product the grid is not drawn.
- renderRecommended() : void
- Renders the grid of the newest products with a "recommended" tag (`ProductTag::$recommendedFlag`); without any such product the grid is not drawn.
- setAppContext() : $this
- setComponentParameter() : void
- Sets a component parameter.
- setCurrencyContext() : $this
- setLanguageContext() : $this
- setLocalizationContext() : $this
- setSettingContext() : $this
- setThemeContext() : $this
- setup() : void
- setupAppContext() : void
- typedComponent() : T
- Returns a subcomponent with a verified type.
- frontPresenter() : BasePresenter|null
- Returns the front presenter the component is attached to (null outside the front end or when detached).
- getTranslator() : ITranslator
- setTemplateFile() : void
- setTranslator() : self
- allowsOutOfStockOrder() : bool
- Returns whether selling out-of-stock goods is allowed (`order.outOfStock.allowed = 1`); the same gate as in the catalog ({@see \App\UI\Front\Eshop\Presenters\Traits\Inits\CatalogOnlyGuardTrait:: allowsOutOfStockOrder()}).
- createMultiplierComponent() : Multiplier<string|int, IComponent>
- Creates a multiplier for the component.
- createNewComponentInstance() : IComponent|null
- Creates a new component instance without adding it to the presenter; used by the multiplier.
- createStandardComponent() : IComponent|null
- Creates a standard component (not a multiplier).
- decorateWithPrices() : void
- Fills in the current price and availability of the products.
- isCatalogOnly() : bool
- Disabled purchasing (`catalog.only = 1`); the same gate as in the cart widget.
- isSetOrderable() : bool
- Returns whether the set can be ordered, with availability from the same source as the list ({@see ProductSetManager::fetchAvailability()}).
- loadNewest() : array<int, ProductSet>
- Loads the `$maxNumber` newest products and decorates them with prices; the common base of {@see self::prepareItems()} and {@see self::prepareDealItem()}.
- prepareDealItem() : void
- Picks the deal of the week by the behavior flag on the tag ({@see \App\Core\Base\Shared\Eshop\Models\Entities\ProductTag::$saleFlag}), not by discount.
- prepareItems() : void
Constants
LIST_INCLUDES
List includes; keep identical to `ProductSetListTrait::LIST_INCLUDES`.
private
mixed
LIST_INCLUDES
= ['translations', 'productState', 'tags.translations', 'asset', 'ratingThread']
The product card (Templates/ProductSet/Items/_item.latte) reads translation, state, tags
and the preview image; without any of them part of the card content silently goes missing.
ratingThread carries denormalized aggregates (ratingCount, ratingAvg), so it is one
extra join; without the include the card would touch a lazy association and the list would
turn into N+1.
SETTINGS_MODULE_ESHOP
Settings module id of the eshop.
private
int
SETTINGS_MODULE_ESHOP
= 4
Properties
$container
public
Container
$container
Attributes
- #[Inject]
$routerDispatcher
public
RouterDispatcher
$routerDispatcher
$translator
public
ITranslator
$translator
Tags
$appContext
protected
AppContext
$appContext
$componentParameters
protected
array<string, mixed>
$componentParameters
= []
component parameters set by setComponentParameter()
$currencyContext
protected
CurrencyContext|null
$currencyContext
= NULL
$deviceDetector
protected
DeviceDetector|null
$deviceDetector
= NULL
$languageContext
protected
LanguageContext|null
$languageContext
= NULL
$localizationContext
protected
LocalizationContext|null
$localizationContext
= NULL
$settingContext
protected
SettingContext
$settingContext
$themeContext
protected
ThemeContext|null
$themeContext
= NULL
$translatorDomain
IMPORTANT: no translator domain. The product card (`Templates/ProductSet/Items/_item.latte`) translates with full keys (`front.eshop.availability.soldOut`, ...) because the presenter renders it too; with a domain the key would be prefixed twice and the card would print the bare `front.eshop.cart.addToCart` instead of the text.
protected
string|null
$translatorDomain
= null
$cart read-only
private
CartManager
$cart
$productSetManager read-only
private
ProductSetManager
$productSetManager
Methods
__call()
Dispatches `renderXyz()` to `render('Xyz', ...$args)`.
public
__call(string $name, array<string|int, mixed> $args) : mixed
Called only when the component has no renderXyz() method of its own.
Parameters
- $name : string
- $args : array<string|int, mixed>
__construct()
public
__construct(ProductSetManager $productSetManager, CartManager $cart) : mixed
Parameters
- $productSetManager : ProductSetManager
- $cart : CartManager
createComponent()
public
createComponent(string $name) : IComponent|null
Parameters
- $name : string
Return values
IComponent|nullgetAppContext()
public
getAppContext() : AppContext
Return values
AppContextgetComponentParameter()
Returns a component parameter, or null when it is not set.
public
getComponentParameter(string $name) : mixed
Parameters
- $name : string
getContainer()
public
getContainer() : Container
Return values
ContainergetCurrencyContext()
public
getCurrencyContext() : CurrencyContext|null
Return values
CurrencyContext|nullgetLanguageContext()
public
getLanguageContext() : LanguageContext|null
Return values
LanguageContext|nullgetLocalizationContext()
public
getLocalizationContext() : LocalizationContext|null
Return values
LocalizationContext|nullgetRouterDispatcher()
public
getRouterDispatcher() : RouterDispatcher
Return values
RouterDispatchergetSettingContext()
public
getSettingContext() : SettingContext
Return values
SettingContextgetSettings()
public
getSettings() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>getThemeContext()
public
getThemeContext() : ThemeContext|null
Return values
ThemeContext|nullhandleAddToCart()
Handles "add to cart" from the product card.
public
handleAddToCart(int $setId[, int $quantity = 1 ]) : void
IMPORTANT: it must live here even though
AddToCartTrait does the same. The card
(Templates/ProductSet/Items/_item.latte) builds the link as {link addToCart!}, which binds
to the component or presenter that renders it. In the /eshop list that is
ProductSetPresenter with the trait; here it is this component, so without an own handler
Nette cannot build the link: production gets href="#error" and debug mode throws
"Unknown signal 'addToCart'".
The behavior matches the presenter variant: AJAX redraws the cart badge and flash, without JS it redirects to the cart.
Parameters
- $setId : int
- $quantity : int = 1
hasServiceByType()
Returns whether the container has a service of the given type.
public
hasServiceByType(string $className) : bool
The name is composed dynamically (module plus component), so class_exists() runs first, and
a true result guarantees a valid type.
Parameters
- $className : string
Tags
Return values
boolinitializeAllContexts()
public
initializeAllContexts() : void
injectDeviceDetector()
public
injectDeviceDetector(DeviceDetector $detector) : void
Parameters
- $detector : DeviceDetector
onCreate()
public
onCreate() : void
render()
public
render([string $style = 'Grid' ][, int $maxNumber = 4 ]) : void
Parameters
- $style : string = 'Grid'
- $maxNumber : int = 4
renderDeal()
Renders the deal of the week: one product with a sale-flag tag (`ProductTag::$saleFlag`).
public
renderDeal([int $lookback = 12 ]) : void
The choice follows the tag meaning, not the discount; a discounted product shows it on its
card but that does not decide the section. Products with an actually scheduled end of the
sale (future price) go first; without any sale-tagged product the template hides the section
(Deal.latte, {if $items}). Priority details: InitTrait::prepareDealItem().
Parameters
- $lookback : int = 12
-
how many of the newest sale-tagged products are searched (DB filter
tags.saleFlag = 1, not PHP filtering after loading)
renderNews()
Renders the grid of the newest products with a "new" tag (`ProductTag::$newFlag`); without any such product the grid is not drawn.
public
renderNews([int $maxNumber = 4 ][, string $style = 'Grid' ]) : void
Parameters
- $maxNumber : int = 4
- $style : string = 'Grid'
renderRecommended()
Renders the grid of the newest products with a "recommended" tag (`ProductTag::$recommendedFlag`); without any such product the grid is not drawn.
public
renderRecommended([int $maxNumber = 4 ][, string $style = 'Grid' ]) : void
Parameters
- $maxNumber : int = 4
- $style : string = 'Grid'
setAppContext()
public
setAppContext(AppContext $appContext) : $this
Parameters
- $appContext : AppContext
Return values
$thissetComponentParameter()
Sets a component parameter.
public
setComponentParameter(string $name, mixed $value) : void
Parameters
- $name : string
- $value : mixed
setCurrencyContext()
public
setCurrencyContext(CurrencyContext|null $currencyContext) : $this
Parameters
- $currencyContext : CurrencyContext|null
Return values
$thissetLanguageContext()
public
setLanguageContext(LanguageContext|null $languageContext) : $this
Parameters
- $languageContext : LanguageContext|null
Return values
$thissetLocalizationContext()
public
setLocalizationContext(LocalizationContext|null $localizationContext) : $this
Parameters
- $localizationContext : LocalizationContext|null
Return values
$thissetSettingContext()
public
setSettingContext(SettingContext $settingContext) : $this
Parameters
- $settingContext : SettingContext
Return values
$thissetThemeContext()
public
setThemeContext(ThemeContext|null $themeContext) : $this
Parameters
- $themeContext : ThemeContext|null
Return values
$thissetup()
public
setup(BasePresenter|BasePresenter $presenter) : void
Parameters
- $presenter : BasePresenter|BasePresenter
setupAppContext()
public
setupAppContext(AppContext $appContext) : void
Parameters
- $appContext : AppContext
typedComponent()
Returns a subcomponent with a verified type.
public
typedComponent(string $name, T> $type) : T
Use it instead of an inline @var above $this['x'] (which does not narrow the type) and
instead of the generic IComponent that $this['x'] returns. See
Form::typedComponent().
Parameters
- $name : string
- $type : T>
Tags
Return values
TfrontPresenter()
Returns the front presenter the component is attached to (null outside the front end or when detached).
protected
frontPresenter() : BasePresenter|null
Return values
BasePresenter|nullgetTranslator()
protected
getTranslator() : ITranslator
Return values
ITranslatorsetTemplateFile()
protected
setTemplateFile(string $dir, string $filename) : void
Parameters
- $dir : string
- $filename : string
setTranslator()
protected
setTranslator(ITranslator $translator) : self
Parameters
- $translator : ITranslator
Return values
selfallowsOutOfStockOrder()
Returns whether selling out-of-stock goods is allowed (`order.outOfStock.allowed = 1`); the same gate as in the catalog ({@see \App\UI\Front\Eshop\Presenters\Traits\Inits\CatalogOnlyGuardTrait:: allowsOutOfStockOrder()}).
private
allowsOutOfStockOrder() : bool
A missing key means disabled; a silently allowed oversell would be the worst possible failure.
Return values
boolcreateMultiplierComponent()
Creates a multiplier for the component.
private
createMultiplierComponent(string $name) : Multiplier<string|int, IComponent>
Parameters
- $name : string
Return values
Multiplier<string|int, IComponent>createNewComponentInstance()
Creates a new component instance without adding it to the presenter; used by the multiplier.
private
createNewComponentInstance(string $baseName[, int|string|null $uniqueKey = null ]) : IComponent|null
Parameters
- $baseName : string
- $uniqueKey : int|string|null = null
Return values
IComponent|nullcreateStandardComponent()
Creates a standard component (not a multiplier).
private
createStandardComponent(string $name) : IComponent|null
Parameters
- $name : string
Return values
IComponent|nulldecorateWithPrices()
Fills in the current price and availability of the products.
private
decorateWithPrices(array<int, ProductSet> $items) : void
IMPORTANT: without this step the card prints no price. getCurrentPrice() is not an entity
column; it is filled in batches from the price list views after the list is loaded, so
findAll() alone returns products with an empty price and the card falls into the "price on
request" branch. The eshop presenter does the same decoration
(BatchDecorationTrait).
There are two batch calls for the whole list, not one query per product, otherwise the home page would suffer N+1.
IMPORTANT: the price list is the base without VAT; the entity stores only the display price
(computed by withVat of each fetchCurrentPrices() row, PriceDisplayHelper), exactly
as the presenter decoration does. A difference would make the promo page show different
prices than the list.
Parameters
- $items : array<int, ProductSet>
isCatalogOnly()
Disabled purchasing (`catalog.only = 1`); the same gate as in the cart widget.
private
isCatalogOnly() : bool
Return values
boolisSetOrderable()
Returns whether the set can be ordered, with availability from the same source as the list ({@see ProductSetManager::fetchAvailability()}).
private
isSetOrderable(int $setId) : bool
Parameters
- $setId : int
Return values
boolloadNewest()
Loads the `$maxNumber` newest products and decorates them with prices; the common base of {@see self::prepareItems()} and {@see self::prepareDealItem()}.
private
loadNewest(int $maxNumber[, array<int, object> $extraFilters = [] ]) : array<int, ProductSet>
$extraFilters (prepareDealItem(): tags.saleFlag = 1, the tag grids: tags.newFlag / tags.recommendedFlag) are added to the eligibility
filters as a DB filter, not PHP filtering after loading. An EqualFilter with a dotted path
through a to-many association makes JoinHelper::resolveColumn() add a left join and phase 1
of the query (GetAllTrait::fetchIdentifiers()) runs with distinct, so a product with two
sale tags is not returned twice and the limit applies exactly to the filtered data.
Parameters
- $maxNumber : int
- $extraFilters : array<int, object> = []
Return values
array<int, ProductSet>prepareDealItem()
Picks the deal of the week by the behavior flag on the tag ({@see \App\Core\Base\Shared\Eshop\Models\Entities\ProductTag::$saleFlag}), not by discount.
private
prepareDealItem(int $lookback) : void
Priority (see LastProductList::renderDeal()):
- Among products with at least one
sale_flag = 1tag (DB filter through the to-manytags.saleFlag, see loadNewest()) the first one with a scheduled future price, i.e. a running sale with a known end (a countdown makes sense). - Otherwise the first sale-tagged product (the sale runs, its end is unknown, no countdown).
- Without any sale-tagged product nothing:
items = []and the template hides the section (Deal.latte,{if $items}). A discount (original > price) is not a selection condition; a discounted product shows it on its card but that does not decide the section.
dealEndsAt is the future price time of the selected item; only priority 1 has one.
Parameters
- $lookback : int
prepareItems()
private
prepareItems(int $maxNumber, array<int, object> $extraFilters) : void
Parameters
- $maxNumber : int
- $extraFilters : array<int, object>
-
selection by a tag flag (
tags.newFlag,tags.recommendedFlag), see loadNewest()