CoolCMS API

ArticleDocumentSource
in package
implements DocumentSourceInterface, HighlightableDocumentSourceInterface, VisibilityConstrainedDocumentSourceInterface

FinalYes

Builds the Elasticsearch documents of an article: one document per (article, language), one index per language (`blog_article_{alias}`), `_id` = `{articleId}_{languageId}`.

ArticleText has a derived identity (languageId plus parent) and no PK of its own.

Visibility is enforced by the database (SearchIdFilter is phaseOneOnly). A deactivated article is removed from the index (empty buildDocuments()); published is an indexed field and the Elasticsearch query pre-narrows by it as a query-time condition (getVisibilityFilters()).

Table of Contents

Interfaces

DocumentSourceInterface
Source of documents for one indexed entity (Article, Advert, Message…).
HighlightableDocumentSourceInterface
Source that can supply highlighted snippets for search results.
VisibilityConstrainedDocumentSourceInterface
Source that can pass its visibility conditions to the ES query.

Constants

INCLUDES  : mixed = ['translations', 'categories', 'tags']
KEY  : mixed = 'blog_article'

Properties

$em  : EntityManagerInterface
$languageManager  : LanguageManager
$languages  : array<int, string>|null
$service  : ArticleService

Methods

__construct()  : mixed
buildDocuments()  : array<string, array<string, array<string, mixed>>>
Builds the documents of an entity as `indexBase => (_id => document)`. An empty array means the entity must not be indexed (deactivated or without an active translation), so it is deleted.
expectedDocumentBounds()  : array{min: int, max: int}
Mirrors the `buildDocuments()` guards: a document exists only for an activated article (`activated` = 1) with at least one active translation. The minimum is the number of such articles (count distinct, so the dot-path join on translations does not multiply rows), the maximum is the minimum times the number of languages (an upper estimate, not every article has all languages active).
fetchExistingIds()  : array<string|int, int>
Returns which of the given ids really exist in the DB; orphan cleanup deletes from ES the documents of entities no longer in the DB.
fetchFresh()  : array<int, object>
Returns fresh entities after commit (service `find` with `useCache: false`). An entity from `completedSave` is not authoritative, because `afterSave` changes the DB through `updateColumn` after the flush. A missing id is omitted.
fetchIdsChangedSince()  : array<string|int, int>
Returns ids of entities changed or created since `$since` (DB, no cache) as the input of the incremental sync. It must cover both insert and update (`insertTime` or `updateDatetime`); mutations outside the ORM (`updateColumn`, raw SQL) are never covered and the nightly full reindex catches them up.
fetchReindexBatch()  : array<int, object>
Returns a batch of entities for a full reindex by key: `id > $afterId`, ordered ascending by id, at most `$limit` (with includes, no cache). The array key is the entity id; the last one becomes the cursor the next part of the reindex continues from.
getAllDocumentIds()  : array<string, array<string|int, string>>
Returns every possible `_id` of the entity across languages as `indexBase => [_id, …]`.
getEntityIdField()  : string
Returns the ES document field holding the entity id (e.g. `articleId`); sync sorts and pages (`search_after`) by it during the orphan scan.
getHighlightFields()  : array<string, array<int, string>>
Highlights the name and perex, exactly what the search list renders (`.list-item__title` / `.list-item__desc`). `content` is not highlighted: it is not in the list, and `number_of_fragments: 0` would pull whole article bodies through Elasticsearch.
getIndexBaseForLanguage()  : string|null
Returns the index base for a language alias (`cs`), null when the language does not exist.
getIndexBases()  : array<string|int, string>
Returns index base names without prefix and version; one per language for translated entities (e.g. `['blog_article_cs', 'blog_article_en']`).
getIndexDefinition()  : array<string, mixed>
Returns the index definition (settings and mappings) for index creation.
getKey()  : string
Returns the source key for the `enabledSources` allowlist and `EsSearcher`, e.g. `blog_article`.
getSearchFields()  : array<string, int|float>
Returns the `multi_match` fields with boosts, e.g. `['name' => 3, 'perex' => 2, 'content' => 1]`.
getVisibilityFilters()  : array<int, array<string, mixed>>
Pre-narrows the Elasticsearch query by the publication date (query-time, cannot go stale).
getLanguages()  : array<int, string>
Assigns the cache only after a successful load: an exception from `findAll()` must not leave the cache marked as loaded and empty.
toIds()  : array<string|int, int>
toPlainText()  : string
`content` and `perex` are sanitized TinyMCE HTML; only plain text goes to the index.

Constants

Properties

Methods

buildDocuments()

Builds the documents of an entity as `indexBase => (_id => document)`. An empty array means the entity must not be indexed (deactivated or without an active translation), so it is deleted.

public buildDocuments(object $entity) : array<string, array<string, array<string, mixed>>>
Parameters
$entity : object
Return values
array<string, array<string, array<string, mixed>>>

expectedDocumentBounds()

Mirrors the `buildDocuments()` guards: a document exists only for an activated article (`activated` = 1) with at least one active translation. The minimum is the number of such articles (count distinct, so the dot-path join on translations does not multiply rows), the maximum is the minimum times the number of languages (an upper estimate, not every article has all languages active).

public expectedDocumentBounds() : array{min: int, max: int}
Return values
array{min: int, max: int}

fetchExistingIds()

Returns which of the given ids really exist in the DB; orphan cleanup deletes from ES the documents of entities no longer in the DB.

public fetchExistingIds(array<string|int, mixed> $ids) : array<string|int, int>
Parameters
$ids : array<string|int, mixed>
Return values
array<string|int, int>

fetchFresh()

Returns fresh entities after commit (service `find` with `useCache: false`). An entity from `completedSave` is not authoritative, because `afterSave` changes the DB through `updateColumn` after the flush. A missing id is omitted.

public fetchFresh(array<string|int, mixed> $ids) : array<int, object>
Parameters
$ids : array<string|int, mixed>
Return values
array<int, object> —

keyed by id

fetchIdsChangedSince()

Returns ids of entities changed or created since `$since` (DB, no cache) as the input of the incremental sync. It must cover both insert and update (`insertTime` or `updateDatetime`); mutations outside the ORM (`updateColumn`, raw SQL) are never covered and the nightly full reindex catches them up.

public fetchIdsChangedSince(DateTimeImmutable $since) : array<string|int, int>
Parameters
$since : DateTimeImmutable
Return values
array<string|int, int>

fetchReindexBatch()

Returns a batch of entities for a full reindex by key: `id > $afterId`, ordered ascending by id, at most `$limit` (with includes, no cache). The array key is the entity id; the last one becomes the cursor the next part of the reindex continues from.

public fetchReindexBatch(int $afterId, int $limit) : array<int, object>
Parameters
$afterId : int
$limit : int
Return values
array<int, object> —

keyed by id, ascending

getAllDocumentIds()

Returns every possible `_id` of the entity across languages as `indexBase => [_id, …]`.

public getAllDocumentIds(int $id) : array<string, array<string|int, string>>

It serves targeted deletion of orphan language documents (orphanRemoval on translations) and delete; delete_by_query must never be used.

Parameters
$id : int
Return values
array<string, array<string|int, string>>

getEntityIdField()

Returns the ES document field holding the entity id (e.g. `articleId`); sync sorts and pages (`search_after`) by it during the orphan scan.

public getEntityIdField() : string
Return values
string

getHighlightFields()

Highlights the name and perex, exactly what the search list renders (`.list-item__title` / `.list-item__desc`). `content` is not highlighted: it is not in the list, and `number_of_fragments: 0` would pull whole article bodies through Elasticsearch.

public getHighlightFields() : array<string, array<int, string>>

The base field comes first (stemming also highlights inflected forms), .folded is the fallback for queries without diacritics. Both are needed: the Czech analysis has stopwords, so a stopword query matches only the .folded variant.

Return values
array<string, array<int, string>>

getIndexBaseForLanguage()

Returns the index base for a language alias (`cs`), null when the language does not exist.

public getIndexBaseForLanguage(string|null $languageAlias) : string|null
Parameters
$languageAlias : string|null
Return values
string|null

getIndexBases()

Returns index base names without prefix and version; one per language for translated entities (e.g. `['blog_article_cs', 'blog_article_en']`).

public getIndexBases() : array<string|int, string>
Return values
array<string|int, string>

getIndexDefinition()

Returns the index definition (settings and mappings) for index creation.

public getIndexDefinition(string $indexBase) : array<string, mixed>
Parameters
$indexBase : string
Return values
array<string, mixed>

getKey()

Returns the source key for the `enabledSources` allowlist and `EsSearcher`, e.g. `blog_article`.

public getKey() : string
Return values
string

getSearchFields()

Returns the `multi_match` fields with boosts, e.g. `['name' => 3, 'perex' => 2, 'content' => 1]`.

public getSearchFields() : array<string, int|float>
Return values
array<string, int|float>

getVisibilityFilters()

Pre-narrows the Elasticsearch query by the publication date (query-time, cannot go stale).

public getVisibilityFilters() : array<int, array<string, mixed>>

An article with published in the future is in the index (publication is not a buildDocuments() guard because it changes without a write), so without this the relevance path counts it in totalCount and it takes a page slot even though the database phase drops it.

Mirrors the front end ArticleListTrait::prepareVisibilityFilters(): DateBeforeFilter('published', new DateTime()) means DATE(published) < today (format Y-m-d, null does not pass), expressed as lt now/d. IMPORTANT: time_zone is required: without it now/d rounds to midnight UTC and between 00:00 and 02:00 Prague time articles published yesterday vanish from the search although the database shows them. The forFree category does not belong here: it depends on user access, so only the database evaluates it.

Return values
array<int, array<string, mixed>>

getLanguages()

Assigns the cache only after a successful load: an exception from `findAll()` must not leave the cache marked as loaded and empty.

private getLanguages() : array<int, string>
Return values
array<int, string> —

languageId => alias

toIds()

private toIds(array<string|int, object> $entities) : array<string|int, int>
Parameters
$entities : array<string|int, object>
Return values
array<string|int, int>

toPlainText()

`content` and `perex` are sanitized TinyMCE HTML; only plain text goes to the index.

private toPlainText(string|null $html) : string
Parameters
$html : string|null
Return values
string
On this page

Search results