It’s not very obvious how to use entities in node types and make entities
selectable in the inspector for a node property or access entity properties
when using nodes in Fusion. With Neos 2.3 there’s a pretty good way of connecting
the two worlds.
Suppose we have the following entity:
/**
* @Flow\Entity
*/
class Product {
/**
* string
*/
protected $title;
// ...
}
And a node type MyPackage:ProductTeaser
where we want to reference a product
to display some properties in the node type.
Let’s start with a new property for a Product
on the node type in a
NodeType.yaml
:
MyPackage:ProductTeaser:
superTypes:
'TYPO3.Neos:Content': true
ui:
label: 'Product teaser'
properties:
product:
type: 'MyPackage\Domain\Model\Product'
ui:
label: 'Product'
inspector:
group: 'resources'
reloadIfChanged: true
We just use the entity type in the node type definition and add a special
setting for Neos on how to render this entity in the HTML markup for the backend
(serialized node information) and specify a default editor using a SelectBoxEditor
.
In the package Settings.yaml
:
TYPO3:
Neos:
userInterface:
inspector:
dataTypes:
'MyPackage\Domain\Model\Product':
typeConverter: 'TYPO3\Neos\TypeConverter\EntityToIdentityConverter'
editor: 'TYPO3.Neos/Inspector/Editors/SelectBoxEditor'
editorOptions:
dataSourceIdentifier: 'mypackage-products'
We specified a data source (see http://neos.readthedocs.io/en/stable/ExtendingNeos/DataSources.html)
here that will fetch the data for the select box from the server. This needs a
custom PHP class that implements TYPO3\Neos\Service\DataSource\DataSourceInterface
:
class ProductSource extends AbstractDataSource
{
/**
* @var ProductRepository
* @Flow\Inject
*/
protected $productRepository;
/**
* @Flow\Inject
* @var PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* @var string
*/
protected static $identifier = 'mypackage-products';
/**
* @inheritDoc
*/
public function getData(NodeInterface $node = null, array $arguments)
{
// Use [] if you don't want to include an "empty" item
$options = [['label' => '-', 'value' => '']];
$products = $this->productRepository->findAll();
foreach ($products as $product) {
$options[] = [
'label' => $product->getTitle(),
// Yes, we actually need to encode the value to match EntityToIdentityConverter that is used to encode the serialized node property!
'value' => json_encode([
'__identity' => $this->persistenceManager->getIdentifierByObject($product),
'__type' => TypeHandling::getTypeForValue($product)
])
];
}
return $options;
}
}
When rendering a ProductTeaser
node you can now access an associated Product
entity after it was set in the inspector:
<div class="product-teaser">
<h2>{product.title}</h2>
</div>
This works because the property type is set to an actual class and can be
deserialized with the type and identity values converted by EntityToIdentityConverter
.
It’s Magic