CoolCMS API

LastProductList extends BaseComponent
in package
uses InitTrait

FinalYes

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

$componentParameters

protected array<string, mixed> $componentParameters = []

component parameters set by setComponentParameter()

$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

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>

createComponent()

public createComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

getComponentParameter()

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
Container

getSettings()

public getSettings() : array<int, array<string, mixed>>
Return values
array<int, array<string, mixed>>

handleAddToCart()

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
phpstan-assert-if-true

class-string $className

Return values
bool

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'

setComponentParameter()

Sets a component parameter.

public setComponentParameter(string $name, mixed $value) : void
Parameters
$name : string
$value : mixed

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
template
Return values
T

frontPresenter()

Returns the front presenter the component is attached to (null outside the front end or when detached).

protected frontPresenter() : BasePresenter|null
Return values
BasePresenter|null

getTranslator()

protected getTranslator() : ITranslator
Return values
ITranslator

setTemplateFile()

protected setTemplateFile(string $dir, string $filename) : void
Parameters
$dir : string
$filename : string

setTranslator()

protected setTranslator(ITranslator $translator) : self
Parameters
$translator : ITranslator
Return values
self

allowsOutOfStockOrder()

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
bool

createMultiplierComponent()

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|null

createStandardComponent()

Creates a standard component (not a multiplier).

private createStandardComponent(string $name) : IComponent|null
Parameters
$name : string
Return values
IComponent|null

decorateWithPrices()

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
bool

isSetOrderable()

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
bool

loadNewest()

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()):

  1. Among products with at least one sale_flag = 1 tag (DB filter through the to-many tags.saleFlag, see loadNewest()) the first one with a scheduled future price, i.e. a running sale with a known end (a countdown makes sense).
  2. Otherwise the first sale-tagged product (the sale runs, its end is unknown, no countdown).
  3. 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()

On this page

Search results