Inserting a form into a block in Drupal? - drupal

Is there any command or method that I can use to insert the contents of a form (e.g. the user registration form) into a block?

In Drupal 7, it looks like this:
function yourmodule_block_view($delta='')
{
switch($delta) {
case 'your_block_name':
$block['subject'] = null; // Most forms don't have a subject
$block['content'] = drupal_get_form('yourmodule_form_function');
break;
}
return $block;
}
The form array returned by drupal_get_form will be automatically rendered.
yourmodule_form_function is a function (in your module or an existing Drupal module) that returns the form array;

drupal_get_form($form_id) - put it in a module's hook_block ($op=='view') or even... shudder... inside a block with PHP filter on.
You need to find the form id first - look for a hidden input with the name form_id within the form. Its value should be the the form id.
Also, you could simply use the Form Block module.

Drupal 8+ solution
Create the form. Then, to create the block use something like this:
<?php
namespace Drupal\my_module\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\my_module\Form\MyForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides the My Block block.
*
* #Block(
* id = "my_block",
* admin_label = #Translation("My Block")
* )
*/
class MyBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The form builder.
*
* #var \Drupal\Core\Form\FormBuilder
*/
protected $formBuilder;
/**
* Constructs a new MyBlock object.
*
* #param array $configuration
* A configuration array containing information about the plugin instance.
* #param string $plugin_id
* The plugin_id for the plugin instance.
* #param mixed $plugin_definition
* The plugin implementation definition.
* #param \Symfony\Component\DependencyInjection\ContainerInterface $container
* Our service container.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ContainerInterface $container) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->formBuilder = $container->get('form_builder');
}
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container
);
}
/**
* {#inheritdoc}
*/
public function build() {
$form = $this->formBuilder->getForm(MyForm::class);
return $form;
// // Or return a render array.
// // in mytheme.html.twig use {{ form }} and {{ data }}.
// return [
// '#theme' => 'mytheme',
// "#form" => $form,
// "#data" => $data,
// ];
}
}

Related

override FieldPluginBase init function Drupal

is it possible to inject a custom service into a class which extends FieldPluginBase ?
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->currentDisplay = $view->current_display;
}
When I try to inject one of my services I get that error :
Fatal error: Declaration of Drupal\xxx_api\Plugin\views\field\NewsViewsField::init(Drupal\views\ViewExecutable $view, Drupal\views\Plugin\views\display\DisplayPluginBase $display, ?array &$options, Drupal\xxx_api\Service\ArticleService $articleService) must be compatible with Drupal\views\Plugin\views\field\FieldPluginBase::init(Drupal\views\ViewExecutable $view, Drupal\views\Plugin\views\display\DisplayPluginBase $display, ?array &$options = NULL)
Thanks by advance for helping :)
Yes this is possible, but you should inject it either in the constructor or via the create() method.
In the same views core module of which you are extending the FieldPluginBase class from there is a RenderedEntity class which is a good example that does so.
So in your case this could look like something in the below. Note that I have used YourService as a placeholder for the service that you are trying to inject:
namespace Drupal\xxx_api\Plugin\views\field;
/**
* Example Views field.
*
* #ViewsField("news_views_field")
*/
class NewsViewsField extends FieldPluginBase {
/**
* Your Service interface.
*
* #var \Drupal\foo\YourServiceInterface
*/
protected $yourService;
/**
* Constructs a NewsViewsField object.
*
* #param array $configuration
* A configuration array containing information about the plugin instance.
* #param string $plugin_id
* The plugin_id for the plugin instance.
* #param mixed $plugin_definition
* The plugin implementation definition.
* #param \Drupal\foo\YourServiceInterface $your_service
* Your Service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, YourServiceInterface $your_service) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
// Inject your service.
$this->yourService = $your_service;
}
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
// Add your service here to pass an instance to the constructor.
$container->get('your.service')
);
}
...
}

I would like to ask about PHPUnit of Drupal 8

I created Block in Drupal 8 with a custom module.
Is it possible to implement this with PHPUnit?
If you can implement it please tell me how.
I want to realize the test with PHPUnit below.
I would be pleased if you could reply just whether it was possible or not.
moduleNameBlock.php
/**
* #file
* create block
*/
namespace Drupal\moduleName\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Url;
/**
*
* Provides a 'testBlock' block.
* #Block(
* id = "test_block",
* admin_label = #Translation("Test"),
* category = #Translation("Menu"),
* )
*/
class moduleNameBlock extends BlockBase {
/**
* {#inheritdoc}
*/
public function build()
{
$build = [];
$url = '';
$nid = '';
$nid = $this->getCurrentUserNode();
if ( !empty($nid) ) {
$url = Url::fromRoute('entity.node.canonical', ['node' => $nid]);
}
$block = [
'#theme' => 'block_theme',
'#url' => $url,
'#nid' => $nid,
'#cache' => [
'max-age' => 0
]
];
$build['test_block'] = $block;
return $build;
}
/**
* The node associated with the user
* #return nid
*/
private function getCurrentUserNode() {
$user_id = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
$nid = $user_id->get('field_name')->getValue();
return $nid[0]['target_id'];
}
}
Yes this is possible by writing a PHPUnit Functional test.
In your module directory create the following structure /tests/src/Functional then create a file like ModuleNameBlockTest.php then you can place the block in the setUp function and create tests to test the block.
<?php
namespace Drupal\Tests\my_module_name\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Class ModuleNameBlockTest.
*
* #package Drupal\Tests\my_module_name\Functional
* #group my_group
*/
class ModuleNameBlockTest extends BrowserTestBase {
/**
* Modules to enable.
*
* #var array
*/
public static $modules = ['block', 'my_module_name'];
/**
* {#inheritdoc}
*/
protected function setUp() {
parent::setUp();
$adminUser = $this->drupalCreateUser(['administer blocks']);
$this->drupalLogin($adminUser);
$this->drupalPlaceBlock('my_block_name');
$this->drupalLogout($adminUser);
}
/**
* Test the block.
*/
public function testMyAwesomeBlock() {
// Your test logic here.
}
}
You can always look into the source code of Drupal for some examples. E.g. UserBlocksTest.php of the core user module.

Symfony 3.3: unable to add and remove items of the same entity with collection form type

I am new to Symfony, so I started with the official tutorial, installed Symfony framework 3.3.2 and worked through the tutorial while customizing entities, controllers, forms and views to my specific needs.
So basically I have an entity named BasePreset, there are already a couple of rows in the DB, and I finally have managed to create a form collection type that renders a list of editable BasePreset entity fields with 'add' and 'remove' links: the 1st adds new blank fields to the list, and every 'remove' link removes the corresponding fields from DOM. Everything according to docs.
So I succeed to update the existing fields (I see the right changes in form HTML after reloading and in the DB as well).
The problem is, add/delete not working. No errors given. Checked in Chrome Dev tools: parameters sent as expected.
I used the following documentation (and a lot of googling of course) regarding the form builder:
http://symfony.com/doc/current/forms.html
https://symfony.com/doc/current/best_practices/forms.html
https://symfony.com/doc/current/form/form_collections.html
https://symfony.com/doc/current/reference/forms/types/collection.html
Now, in this doc is stated:
You have to create both addTag() and removeTag() methods, otherwise
the form will still use setTag() even if by_reference is false. You'll
learn more about the removeTag() method later in this article.
At this moment I don't have any referenced subentity as it is described in the examples. I just want be able to edit the same plain entity, including adding new items and deleting existing. Maybe I'm wrong, but this seems as kind of a trivial basic goal in my mind. I don't understand how to properly add 'setBasePreset' and 'removeBasePreset' methods to the 'BasePreset' entity itself.
Of course I can skip using the form builder, but I'd like to leverage its power as a part of the framework. Any advice, example, maybe pointing to some relevant doc/tutorial that I missed - will be greatly appreciated.
Post example (with one addition and one removal):
base_presets[base_presets][5][name]:new_preset
base_presets[base_presets][5][description]:new, not really added
base_presets[base_presets][0][name]:ffffas44432df
base_presets[base_presets][0][description]:asdfffff2333
base_presets[base_presets][2][name]:ffffasdf2222
base_presets[base_presets][2][description]:asdff3fff2333
base_presets[base_presets][3][name]:yoyoshka
base_presets[base_presets][3][description]:nananaf
base_presets[base_presets][4][name]:123fffdsaasdf
base_presets[base_presets][4][description]:pop123
base_presets[_token]:H2QwRHdvZW1WAdc6VTONnspxvH1U-oC8rCEEprDdMCQ
The 'BasePresetsType' class:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class BasePresetsType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('base_presets', CollectionType::class, array(
'entry_type' => BasePresetType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false // also tried 'true' but without any success
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
));
}
}
The 'BasePresetType' class:
<?php
namespace AppBundle\Form;
use AppBundle\Entity\BasePreset;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class BasePresetType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', TextType::class)
->add('description', TextareaType::class);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'data_class' => BasePreset::class,
]);
}
}
The controller method:
<?php
// ...
/**
* #Route("/admin/list_base_presets", name="list_base_presets")
*/
public function listBasePresetsAction(Request $request, EntityManagerInterface $em, LoggerInterface $logger) {
$base_presets = $em->getRepository("AppBundle\Entity\BasePreset")->findAll();
$form = $this->createForm(BasePresetsType::class, array('base_presets' => $base_presets));
$form->handleRequest($request);
$current_route = $request->get('_route');
if ($form->isSubmitted() && $form->isValid()) {
foreach ($base_presets as $base_preset) {
$em->persist($base_preset);
}
$em->flush();
// ... do other work - like sending an email, etc..
// maybe set a 'flash' success message for the user
return $this->redirectToRoute($current_route);
}
return $this->render('manage_params/list_base_presets.html.twig', array(
'form' => $form->createView()
));
}
The view:
{% extends "base.html.twig" %}
{% block body %}
{{ form_start(form) }}
{{ form_label(form.base_presets) }}
{{ form_errors(form.base_presets) }}
<ul class="base-presets" data-prototype="{{ form_widget(form.base_presets.vars.prototype)|e('html_attr') }}">
{% for base_preset in form.base_presets %}
<li class="base-preset-item">
{{ form_errors(base_preset) }}
{{ form_widget(base_preset) }}
</li>
{% endfor %}
<li class="form-submit">
<input type="submit" value="Submit" class="btn btn-default pull-right" />
</li>
</ul>
{{ form_end(form) }}
{% endblock %}
{% block javascripts %}
<script>
var $collection_holder;
// Setup an "add a base preset" link
var $add_base_preset_link = $('Add a base preset');
var $new_link_li = $('<li></li>').append($add_base_preset_link);
$(document).ready(function () {
// Get the ul that holds the collection of base presets
$collection_holder = $('ul.base-presets');
// add the "add a base preset" anchor and li to the tags ul
$collection_holder.prepend($new_link_li);
// count the current form inputs we have, use that as the new index when inserting a new item
$collection_holder.data('index', $collection_holder.find('li.base-preset-item').length);
$add_base_preset_link.on('click', function (e) {
e.preventDefault();
addBasePresetForm($collection_holder, $new_link_li);
});
// add a delete link to all of the existing base presets form li elements
$collection_holder.find('li.base-preset-item').each(function () {
addBasePresetFormDeleteLink($(this));
});
});
function addBasePresetForm($collection_holder, $new_link_li) {
// Get the data-prototype
var prototype = $collection_holder.data('prototype');
// Get the new index
var index = $collection_holder.data('index');
// Replace '__name__' in the prototype's HTML to instead be a number based on how many items we have
var new_form = prototype.replace(/__name__/g, index);
// increment the index for the next item
$collection_holder.data('index', index + 1);
// Display the form in the page in an li, before the "Add a base preset" link li
var $new_form_li = $('<li class="base-preset-item"></li>').append(new_form);
$new_link_li.after($new_form_li);
addBasePresetFormDeleteLink($new_form_li);
}
function addBasePresetFormDeleteLink($base_preset_form_li) {
var $remove_form_a = $('Delete this base preset');
$base_preset_form_li.append($remove_form_a);
$remove_form_a.on('click', function (e) {
e.preventDefault();
$base_preset_form_li.remove();
})
}
</script>
{% endblock %}
And, finally, the longest listing - the 'BasePreset' entity class:
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* Class BasePreset
* #package AppBundle\Entity
*
* #ORM\Entity
* #ORM\Table(name="base_presets")
* #UniqueEntity(fields="name", message="There should be only one (unique) base preset")
*/
class BasePreset {
/**
* #ORM\OneToMany(targetEntity="BaseParamsGroup", mappedBy="base_preset")
*/
private $base_params_groups;
/**
* #var int
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=100)
*/
private $name;
/**
* #var string
*
* #Assert\NotBlank()
* #ORM\Column(type="string", length=4000)
*/
private $description;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime")
*/
private $created;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime", columnDefinition="TIMESTAMP on update CURRENT_TIMESTAMP")
*/
private $updated;
public function __construct() {
$this->base_params_groups = new ArrayCollection();
$this->created = new \DateTime();
$this->updated = new \DateTime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return BasePreset
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set description
*
* #param string $description
*
* #return BasePreset
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set created
*
* #param \DateTime $created
*
* #return BasePreset
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* #param \DateTime $updated
*
* #return BasePreset
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* #return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Add baseParamsGroup
*
* #param \AppBundle\Entity\BaseParamsGroup $baseParamsGroup
*
* #return BasePreset
*/
public function addBaseParamsGroup(\AppBundle\Entity\BaseParamsGroup $baseParamsGroup)
{
$this->base_params_groups[] = $baseParamsGroup;
return $this;
}
/**
* Remove baseParamsGroup
*
* #param \AppBundle\Entity\BaseParamsGroup $baseParamsGroup
*/
public function removeBaseParamsGroup(\AppBundle\Entity\BaseParamsGroup $baseParamsGroup)
{
$this->base_params_groups->removeElement($baseParamsGroup);
}
/**
* Get baseParamsGroups
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getBaseParamsGroups()
{
return $this->base_params_groups;
}
}
Since you use form without Data class, you need to access submitted data directly from form object:
foreach ($form->get('base_presets')->getData() as $base_preset) {
$em->persist($base_preset);
}
Update:
That work with managing existing and persisting new ones. If you need to remove entities, you can compare entities loaded from DB with submitted ones, and then remove filtered.

Automatic translation before serialization in symfony2 with JMSSerializerBundle

I have some logic to apply after getting entities from database( by findAll() ) and before serializing the result to json.
I want to add translation on some fields. I know that I can do it manually by iterating on each entity and apply my logic in controller. But I need a better way to do it.
Is there a suggestions to make this automatic ?
I've got similar problem and tried to resolve this using custom handler but with no success, so i created compiler pass and override JsonSerializationVisitor where string values are serialized with TranslatableJsonSerializationVisitor class:
namespace tkuska\DemoBundle\Serialization;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\Context;
class TranslatableJsonSerializationVisitor extends JsonSerializationVisitor
{
/**
* #var \Symfony\Component\Translation\Translator;
*/
private $translator;
public function visitString($data, array $type, Context $context)
{
if (in_array('translatable', $type['params'])) {
return (string) $this->translator->trans($data);
}
return (string) $data;
}
public function setTranslator(\Symfony\Component\Translation\Translator $translator)
{
$this->translator = $translator;
}
}
and compiler:
namespace tkuska\DemoBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class TranslatableJsonSerializationCompiler implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$jmsJsonSerializationVisitor = $container->getDefinition('jms_serializer.json_serialization_visitor');
$jmsJsonSerializationVisitor->setClass('tkuska\DemoBundle\Serialization\TranslatableJsonSerializationVisitor');
$jmsJsonSerializationVisitor->addMethodCall('setTranslator', array(new Reference('translator')));
}
}
in entity i set annotation for type 'string' with param 'translatable'
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<'translatable'>")
* #return order status
*/
public function getOrderStatus(){
return 'status.ordered';
}
Of course 'status.ordered' is my translation key.
Thank you #zizoujab. Very useful post. I made a small improvement to it to call parent method and to unset my parameters, so that i can use this way of altering data on more complex types like array, that already have parameters and used
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<translatable>")
* #return order status
*/
instead of
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<'translatable'>")
* #return order status
*/
to convert the string 'translatable' into a parameter name instead of parameter value thus you can pass more complex parameters like this, and allow me to unset this parameters before calling the parent method.
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<translatable<set of parameters>>")
* #return order status
*/
Code:
<?php
namespace Mktp\DefaultBundle\Service\JmsSerializer;
use JMS\Serializer\Context;
use JMS\Serializer\JsonSerializationVisitor;
use Symfony\Component\Translation\Translator;
class TranslatableJsonSerializationVisitor extends JsonSerializationVisitor
{
/**
* #var Translator;
*/
private $translator;
/**
* #param string $data
* #param array $type
* #param Context $context
* #return string
*/
public function visitString($data, array $type, Context $context)
{
$translatable = $this->getParameters('translatable', $type['params']);
if (count($translatable)) {
$data = (string)$this->translator->trans($data);
}
return parent::visitString($data, $type, $context);
}
/**
* #param array $data
* #param array $type
* #param Context $context
* #return array|\ArrayObject|mixed
*/
public function visitArray($data, array $type, Context $context)
{
$translatable = $this->getParameters('translatable', $type['params']);
if (count($translatable)) {
foreach ($data as $key => $value) {
if (is_string($value)) {
$data[$key] = (string)$this->translator->trans($value);
}
}
}
return parent::visitArray($data, $type, $context);
}
/**
* #param Translator $translator
*/
public function setTranslator(Translator $translator)
{
$this->translator = $translator;
}
/**
* #param string $type
* #param array $parameters
* #param bool $unsetParameters
* #return array
*/
protected function getParameters($type, &$parameters, $unsetParameters = true)
{
$result = array();
foreach ($parameters as $key => $parameter) {
if ($parameter['name'] == $type) {
$result[] = $parameter;
if ($unsetParameters) {
unset($parameters[$key]);
}
}
}
$parameters = array_values($parameters);
return $result;
}
}

How to know if a form field is translatable

Is there a shortcut to know if an entity field has the #Gedmo\Translatable property set, let say when rendering a form, or displaying entity values ?
For example, having this field :
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #Gedmo\Translatable
*/
private $name;
While displaying the entity, I'd like to know if a field is translatable, by doing something like this (pseudo-code idea of what it could be or look like in twig templates)
{% entity.title in entity.translatable.fields %}
Note : The real idea behind this is to automatically display a marker on translatable form field.
Translatable Behavior extension for Doctrine2
You can create a Twig extension:
class TranslatableTypeExtension extends AbstractTypeExtension
{
/**
* #var ObjectManager
*/
private $om;
/**
* #var TranslatableListener
*/
private $listener;
/**
* #param ObjectManager $om
*/
public function __construct(ObjectManager $om, TranslatableListener $listener )
{
$this->om = $om;
$this->listener = $listener;
}
private function isTranslatableField($object, $name)
{
$config = $this->listener->getConfiguration($this->om, get_class($object));
if (isset($config['fields']) && in_array($name, $config['fields']) )
return true;
return false;
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
if ( $form->getParent() == null )
return;
if ( is_object($form->getParent()->getData())) {
if ( $this->isTranslatableField($form->getParent()->getData(), $form->getName()) )
$view->vars['field_translatable'] = true;
}
}
/**
* Returns the name of the type being extended.
*
* #return string The name of the type being extended
*/
public function getExtendedType()
{
return 'field';
}
}
Load this extension as follows:
my_extension.translatable_type_extension:
class: Acme\DemoBundle\Form\Extension\TranslatableTypeExtension
arguments: ["#doctrine.orm.entity_manager", "#gedmo.listener.translatable"]
tags:
- { name: form.type_extension, alias: field }
In your twig templates you could use something like this:
{% if field_translatable is defined and field_translatable %} Translatable field {% endif %}
In your entity repository, assuming you extend the TranslationRepository, you could create a custom function that retrieves fields that have translations. You could create a custom method in your repository along the lines of
use use Doctrine\ORM\Query;
use Gedmo\Translatable\Entity\Repository\TranslationRepository;
class MyEntityRepository extends TranslationRepository
{
public function getTranslatableFieldsByClass($className)
{
$translationMeta = $this->getClassMetadata();
$qb = $this->_em->createQueryBuilder();
$qb->select('trans.field')
->from($translationMeta->rootEntityName, 'trans')
->where('trans.objectClass = :entityClass')
->groupBy('trans.field');
$q = $qb->getQuery();
$data = $q->execute(
array('entityClass' => $className),
Query::HYDRATE_ARRAY
);
return (array) $data;
}
}
Then load the results into your template, and use a similar 'in' clause like you have mentioned above.
$translatableFields = $this->getDoctrine()->getRepository('MyBundle:MyTranslatableEntity')->getTranslatableFieldsByClass(get_class($myTranslatableEntity));
I have the same requirement altough accepted answer won't work for me since it relies on already translated fields. Since I want it even if db is empty I came up with solution based on Gedmo ExtensionMetadataFactory. (solution is written on SF 2.8 and PHP 7.1)
[...]
use Doctrine\ORM\EntityManager;
use Gedmo\Translatable\TranslatableListener;
/**
* Helper Class TranslatableFieldsHelper - allow to get array of translatable fields for given entity class.
*
* #package [...]
* #author [...]
*/
class TranslatableFieldsHelper
{
/**
* #var TranslatableListener
*/
protected $listener;
/**
* #var EntityManager
*/
protected $em;
/**
* TranslatableFieldsHelper constructor.
* #param TranslatableListener $listener
* #param EntityManager $em
*/
public function __construct(TranslatableListener $listener, EntityManager $em)
{
$this->listener = $listener;
$this->em = $em;
}
/**
* Get translatable fields list of given class
*
* #param string $class
* #return array
*/
public function getTranslatableFields(string $class): array
{
$config = $this->listener->getConfiguration($this->em, $class);
return $config && isset($config['fields']) && is_array($config['fields']) ? $config['fields'] : [];
}
}
After implementing this class simply register it as service:
_alias_:
class: _class_
arguments: ['#stof_doctrine_extensions.listener.translatable', '#doctrine.orm.default_entity_manager']
And use it:
$this->container->get(__alias__)->getTranslatableFields(__your_entity_class__);
EDIT:

Resources