Skip to content

Commit

Permalink
Add content fields to ArticleViewDocument (#558)
Browse files Browse the repository at this point in the history
* Add content fields to index

* Fix lint error

* Fix test

* Add documentation
  • Loading branch information
Prokyonn authored Apr 19, 2021
1 parent d4c0f2c commit 5610e00
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 2 deletions.
25 changes: 25 additions & 0 deletions Document/ArticleViewDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,13 @@ class ArticleViewDocument implements ArticleViewDocumentInterface
*/
protected $targetWebspace;

/**
* @var string[]
*
* @Property(type="text")
*/
protected $contentFields;

/**
* @param string $uuid
*/
Expand Down Expand Up @@ -868,4 +875,22 @@ public function setTargetWebspace($targetWebspace)

return $this;
}

/**
* {@inheritdoc}
*/
public function getContentFields()
{
return $this->contentFields;
}

/**
* {@inheritdoc}
*/
public function setContentFields(array $contentFields)
{
$this->contentFields = $contentFields;

return $this;
}
}
12 changes: 12 additions & 0 deletions Document/ArticleViewDocumentInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -508,4 +508,16 @@ public function getTargetWebspace();
* @return $this
*/
public function setTargetWebspace($targetWebspace);

/**
* @return string[]
*/
public function getContentFields();

/**
* @param string[] $searchableContent
*
* @return $this
*/
public function setContentFields(array $contentFields);
}
73 changes: 73 additions & 0 deletions Document/Index/ArticleIndexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Sulu\Bundle\ArticleBundle\Document\Index;

use Metadata\PropertyMetadata;
use ONGR\ElasticsearchBundle\Collection\Collection;
use ONGR\ElasticsearchBundle\Service\Manager;
use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
Expand All @@ -34,6 +35,7 @@
use Sulu\Component\Content\Document\LocalizationState;
use Sulu\Component\Content\Document\WorkflowStage;
use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;
use Sulu\Component\Content\Metadata\StructureMetadata;
use Sulu\Component\DocumentManager\DocumentManagerInterface;
use Sulu\Component\DocumentManager\Exception\DocumentManagerException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
Expand Down Expand Up @@ -246,6 +248,7 @@ protected function createOrUpdateArticle(
}
}

$article->setContentFields($this->getContentFields($structureMetadata, $document));
$article->setContentData(json_encode($document->getStructure()->toArray()));

$article->setMainWebspace($this->webspaceResolver->resolveMainWebspace($document));
Expand All @@ -256,6 +259,76 @@ protected function createOrUpdateArticle(
return $article;
}

protected function getContentFields(StructureMetadata $structure, ArticleDocument $document)
{
$tag = 'sulu.search.field';
$contentFields = [];
foreach ($structure->getProperties() as $property) {
if (method_exists($property, 'getComponents') && \count($property->getComponents()) > 0) {
$blocks = $document->getStructure()->getProperty($property->getName())->getValue();
if (isset($blocks['hotspots'])) {
$blocks = $blocks['hotspots'];
}
$contentFields = array_merge($contentFields, $this->getBlockContentFieldsRecursive($blocks, $document, $property, $tag));
} elseif ($property->hasTag($tag)) {
$value = $document->getStructure()->getProperty($property->getName())->getValue();
if (is_string($value) && '' !== $value) {
$contentFields[] = strip_tags($value);
}
}
}

return $contentFields;
}

/**
* @return string[]
*/
private function getBlockContentFieldsRecursive(array $blocks, ArticleDocument $document, $blockMetaData, $tag)
{
$contentFields = [];
foreach ($blockMetaData->getComponents() as $component) {
/** @var PropertyMetadata $componentProperty */
foreach ($component->getChildren() as $componentProperty) {
if (method_exists($componentProperty, 'getComponents') && \count($componentProperty->getComponents()) > 0) {
$filteredBlocks = array_filter($blocks, function($block) use ($component) {
return $block['type'] === $component->getName();
});

foreach ($filteredBlocks as $filteredBlock) {
if (isset($filteredBlock['hotspots'])) {
$filteredBlock = $filteredBlock['hotspots'];
}
$contentFields = array_merge(
$contentFields,
$this->getBlockContentFieldsRecursive(
$filteredBlock[$componentProperty->getName()],
$document,
$componentProperty,
$tag
)
);
}
}

if (false === $componentProperty->hasTag($tag)) {
continue;
}

foreach ($blocks as $block) {
if ($block['type'] === $component->getName()) {
$blockValue = $block[$componentProperty->getName()];
if (\is_string($blockValue) && '' !== $blockValue) {
$contentFields[] = strip_tags($blockValue);
}
}
}
}
}

return $contentFields;
}

/**
* Returns view-document from index or create a new one.
*
Expand Down
19 changes: 19 additions & 0 deletions Resources/doc/article-view-document.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,28 @@ is requested (content-types, smart-content, ...).
| targetWebspace | string | Recommended webspace key |
| mainWebspace | string | Configured main webspace |
| additionalWebspaces | string[] | Configured additional webspaces |
| contentFields | string[] | Contains content properties tagged with `sulu.search.field` |

The `content` and `view` property is represented by a proxy to avoid resolving data where it is not needed.

### ContentFields

The content of the property `contentFields` can be customized. All properties which are tagged with
`sulu.search.field` in the xml configuration, are automatically added to this field. The main purpose of this field
is to give the developer enough data and flexibility to implement search functionality via e.g. a `SearchController`.

Example:
```xml
<property name="text" type="text_editor" mandatory="true">
<meta>
<title lang="en">Text</title>
<title lang="de">Text</title>
</meta>

<tag name="sulu.search.field"/>
</property>
```

## How to extend?

To extend the indexed data you can extend the `ArticleViewDocument`. This can be achieved by performing the following
Expand Down
4 changes: 2 additions & 2 deletions Tests/Functional/Controller/TemplateControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public function testGet()
$this->assertHttpStatusCode(200, $client->getResponse());

$response = json_decode($client->getResponse()->getContent(), true);
$this->assertEquals(2, $response['total']);
$this->assertCount(2, $response['_embedded']);
$this->assertEquals(3, $response['total']);
$this->assertCount(3, $response['_embedded']);
$this->assertContains(
[
'internal' => false,
Expand Down
37 changes: 37 additions & 0 deletions Tests/Functional/Document/Index/ArticleIndexerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,43 @@ public function testSetUnpublished()
$this->assertFalse($viewDocument->getPublishedState());
}

public function testIndexTaggedProperties()
{
$data = [
'title' => 'Test Article Title',
'pageTitle' => 'Test Page Title',
'article' => '<p>Test Article</p>',
'article_2' => '<p>should not be indexed</p>',
'blocks' => [
[
'type' => 'title-with-article',
'title' => 'Test Title in Block',
'article' => '<p>Test Article in Block</p>',
],
],
];

$article = $this->createArticle($data, $data['title'], 'default_with_search_tags');
$this->documentManager->clear();

$document = $this->documentManager->find($article['id'], $this->locale);
$this->indexer->index($document);
$this->indexer->flush();

$viewDocument = $this->findViewDocument($article['id']);
$contentFields = $viewDocument->getContentFields();

$this->assertSame($article['id'], $viewDocument->getUuid());
$this->assertSame($data, json_decode($viewDocument->getContentData(), true));

$this->assertCount(5, $contentFields);
$this->assertContains('Test Article Title', $contentFields);
$this->assertContains('Test Page Title', $contentFields);
$this->assertContains('Test Article', $contentFields);
$this->assertContains('Test Title in Block', $contentFields);
$this->assertContains('Test Article in Block', $contentFields);
}

public function testIndexContentData()
{
$data = [
Expand Down
45 changes: 45 additions & 0 deletions Tests/app/Resources/articles/default_with_search_tags.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" ?>
<template xmlns="http://schemas.sulu.io/template/template"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/template-1.0.xsd">

<key>default_with_search_tags</key>

<view>::default</view>
<controller>SuluWebsiteBundle:Default:index</controller>
<cacheLifetime>2400</cacheLifetime>

<tag name="sulu_article.type" type="blog"/>

<properties>
<property name="title" type="text_line" mandatory="true">
<tag name="sulu.search.field"/>
</property>

<property name="pageTitle" type="text_line">
<tag name="sulu.search.field"/>
</property>

<property name="article" type="text_editor">
<tag name="sulu.search.field"/>
</property>

<property name="article_2" type="text_editor"/>

<block name="blocks" default-type="title-with-article">
<types>
<type name="title-with-article">
<properties>
<property name="title" type="text_line">
<tag name="sulu.search.field"/>
</property>

<property name="article" type="text_editor">
<tag name="sulu.search.field"/>
</property>
</properties>
</type>
</types>
</block>
</properties>
</template>

0 comments on commit 5610e00

Please sign in to comment.