How can i access the id of a sonata admin entity. suppose i have an entity EndPoint which have a function getProject(), how can i get the id of that project. I tried the code bellow but this gave me the following error: Attempted to call an undefined method named "getProject" of class "ContainerAgbGotv\srcApp_KernelDevDebugContainer".
class EndPointAdmin extends AbstractAdmin{
protected function configureFormFields(FormMapper $form)
{ $container = $this->getConfigurationPool()->getContainer();
$em = $container->getProject();
$array = [];
foreach ($em as $ems) {
if (!empty($ems->getEnv())) {
$array[$ems->getEnv()] = $ems->getEnv();
}}
$result = array_unique($array);
$form
->add('name',ChoiceType::class, [
'choices'=> $result,
'placeholder' => 'Choose an option',
'required' => false
])
->add('ipAdress',TextType::class)
->add('port',IntegerType::class)
;
}
thanks for helping.
In an admin you will have access to
$this->getSubject()
which will return you the current entity for that admin. It's worth noting configureFormFields is used for both creating and editing an entity so getSubject() may return null.
Related
We are using Symfony Forms for our API to validate request data. At the moment we are facing a problem with the CollectionType which is converting the supplied value null to an empty array [].
As it is important for me to differentiate between the user suppling null or an empty array I would like to disable this behavior.
I already tried to set the 'empty_data' to null - unfortunately without success.
This is how the configuration of my field looks like:
$builder->add(
'subjects',
Type\CollectionType::class,
[
'entry_type' => Type\IntegerType::class,
'entry_options' => [
'label' => 'subjects',
'required' => true,
'empty_data' => null,
],
'required' => false,
'allow_add' => true,
'empty_data' => null,
]
);
The form get's handled like this:
$data = $apiRequest->getData();
$form = $this->formFactory->create($formType, $data, ['csrf_protection' => false, 'allow_extra_fields' => true]);
$form->submit($data);
$formData = $form->getData();
The current behavior is:
Input $data => { 'subjects' => null }
Output $formData => { 'subjects' => [] }
My desired behavior would be:
Input $data => { 'subjects' => null }
Output $formData => { 'subjects' => null }
After several tries I finally found a solution by creating a From Type Extension in combination with a Data Transformer
By creating this form type extension I'm able to extend the default configuration of the CollectionType FormType. This way I can set a custom build ModelTransformer to handle my desired behavior.
This is my Form Type Extension:
class KeepNullFormTypeExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [CollectionType::class];
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->addModelTransformer(new KeepNullDataTransformer());
}
}
This one needs to be registered with the 'form.type_extension' tag in your service.yml:
PrivateApiBundle\Form\Extensions\KeepNullFormTypeExtension:
class: PrivateApiBundle\Form\Extensions\KeepNullFormTypeExtension
tags: ['form.type_extension']
Please note that you still use the CollectionType in your FormType and not the KeepNullFormTypeExtension as Symfony takes care about the extending...
In the KeepNullFormTypeExtension you can see that I set a custom model transformer with addModelTransformer which is called KeepNullDataTransformer
The KeepNullDataTransformer is responsible for keeping the input null as the output value - it looks like this:
class KeepNullDataTransformer implements DataTransformerInterface
{
protected $initialInputValue = 'unset';
/**
* {#inheritdoc}
*/
public function transform($data)
{
$this->initialInputValue = $data;
return $data;
}
/**
* {#inheritdoc}
*/
public function reverseTransform($data)
{
return ($this->initialInputValue === null) ? null : $data;
}
}
And that's it - this way a supplied input of the type null will stay as null.
More details about this can be found in the linked Symfony documentation:
https://symfony.com/doc/current/form/create_form_type_extension.html
https://symfony.com/doc/2.3/cookbook/form/data_transformers.html
I'm trying since several days to find out how to ask to easyadmin to store the author_id (or user_id to be specific), but I can't find by myself.
Basicly, by using the createListQueryBuilder function in a specific controller to override the easyadmin controller, I found the way to display for each logged user, the articles they created.
Now I'm locked since several days as i dont know how to tell to easyadmin to store the currently logged author_id (or user_id),the solution is to get the author_id with $this->getUser()->getId() that's ok, but where or how can i link this with my author_id before persisting it to the database
For now if i use this code below the author can add manually its author_id but that's not safe:
{ property: 'user', type: 'entity', type_options: { class: 'AppBundle\Entity\User' ,multiple: false,expanded: true} }
I would like to use an input hidden or any other way that could be more safe
Thank you any anybody could help me
EDIT:it was the hell to find the solution but i finaly found the way to display ONLY THE CURRENT LOGGED USER(who can be a basic admin or contributor if you prefer) or THE FULL LIST OF REGISTERED USERS IN DATABASE if the current logged user as a specific role (in this case that's the website main admin)
here is the solution:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\User;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations\Route;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use AlterPHP\EasyAdminExtensionBundle\EasyAdminExtensionBundle;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AdminController as BaseAdminController;
/**
* Article controller.
*
* #Route("articles")
*/
class ArticlesController extends BaseAdminController
{
/**
* #Route("/", name="easyadmin")
*/
public function indexAction(Request $request)
{
return parent::indexAction($request);
}
public function createListQueryBuilder($entityClass, $sortDirection, $sortField = null, $dqlFilter = null)
{
if(!$this->getUser()->hasRole('ROLE_DIRECTION_ADMIN'))
{
$response = parent::createListQueryBuilder('Articles', $sortDirection, $sortField, $dqlFilter); // TODO: Change the autogenerated stub
$response->where('entity.userId = :userId')->setParameter('userId', $this->getUser()->getId());
return $response;
}
$response = parent::createListQueryBuilder('Articles', $sortDirection, $sortField, $dqlFilter); // TODO: Change the autogenerated stub
return $response;
}
public function createEntityFormBuilder($entity, $view)
{
$formBuilder = parent::createEntityFormBuilder($entity, $view);
// Here I overwrite field to be disabled
if(!$this->getUser()->hasRole('ROLE_DIRECTION_ADMIN'))
{
$formBuilder->add('user', EntityType::class, array(
'class' => 'AppBundle:User',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.id = :id')->setParameter('id',$this->getUser()->getId());
},
));
return $formBuilder;
}
$formBuilder->add('user', EntityType::class, array(
'class' => 'AppBundle:User',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.username', 'asc');
},
));
return $formBuilder;
}
}
1/i created a custom ArticlesController and added 1 method to display the logged user article list if they have a low level role,and display all the website articles if the user role is high
2/ i created a second method which add the currently logged user_id field to the easyadmin formbuilder if the user has a low level role(a contributor for example) OR a full list of users in case the currently logged user is the website main admin
I am sure it is a small error but I cannot find it.
I am trying to follow the official doc and implement an event listener on the pre_serialize event.
My service declaration:
<service id="app.question_serializer_subscriber" class="AppBundle\Serializer\QuestionSerializerSubscriber">
<tag name="jms_serializer.event_subscriber"/>
</service>
My subscriber:
<?php
namespace AppBundle\Serializer;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
class QuestionSerializerSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'method' => 'onPreSerialize',
)
);
}
public function onPreSerialize(ObjectEvent $event)
{
die('in event');
}
}
And my controller:
$question = $repo->findLastVersionByQuestionId((int) $questionId);
$serializer = SerializerBuilder::create()->build();
$context = new SerializationContext();
return new JsonResponse(json_decode(
$serializer->serialize(
$question,
'json',
$context
),
true
));
When I access the route my entity Question is serialized and displayed, but why does the die('in event'); is not displayed ?
Maybe it has a relation with the fact that my object is a Doctrine entity (issue 666 or PR 677 )
I finally find the issue. The problem is
$serializer = SerializerBuilder::create()->build();
This does not work but this does:
$serializer = $this->get('jms_serializer');
Try adding the class attribute, as example:
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'class' => 'FQCN_class_name',
'method' => 'onPreSerialize',
)
);
}
Another difference regarding the doc is in the argument of the class method: you should use PreSerializeEvent instead of ObjectEvent:
So like this:
public function onPreSerialize(PreSerializeEvent $event)
{
// ...
}
Check your service is correctly load from the container as example with the console command:
php app/console debug:container --tag=jms_serializer.event_subscriber
Hope this help
I'm developing the Admin Panel for my website, and one part of it is managing users. I'm using FOSUserBundle for those tasks.
I'm using custom template (AdminLTE) for forms, and I cannot add a dropdown to select roles for user when I add a new one.
In UserType.php
$builder->add('roles', 'choice', array(
'label' => 'User Role',
'choices' => $this->roleHelper->getRolesForForm()
));
In WCB\SaleBundle\Helper\RoleHelper.php
...
public function getRolesForForm()
{
$roleList = self::flatArray($this->rolesHierarchy);
$roles = array();
foreach ($roleList as $roleId => $roleName) {
if ($roleId == 'ROLE_ADMIN') {
$roles[$roleName] = 'Admin';
}
if ($roleId == 'ROLE_USER') {
$roles[$roleName] = 'User';
}
}
return $roles;
}
...
Above getRolesForForm() function will return this array, which is correct format for using with Symfony's choice field type:
Array
(
[ROLE_ADMIN] => Admin
[ROLE_USER] => User
)
And the form's not working anymore, with this exception:
The value of type "array" cannot be converted to a valid array key.
When I add 'multiple' = true to form builder, it works, but it's not a dropdown. It's HTML select box which allow multiple selection.
$builder->add('roles', 'choice', array(
'label' => 'User Role',
'choices' => $this->roleHelper->getRolesForForm(),
'multiple' => true
));
I think, for role selection, it should be a dropdown, not a multiple-selection box. How can I achieve this? Anything wrong with my code? Thank you :)
I think it should be a multiple-selection box indeed.
Take into account that a user can (and usually will) have several roles. In your case, a user who has ROLE_ADMIN also has ROLE_USER (ROLE_ADMIN "includes" ROLE_USER if you are using FOSUserBundle).
Not really sure if this may be the problem since I don't have your full code and I am not sure what self::flatArray($this->rolesHierarchy) is returning but please notice that you are using $roleName, not $roleId as your array key. If $roleName is not a string then you will get this problem.
I faced same issue lately and this is my work around (I created DataTransformer):
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
$builder->get('roles')->addModelTransformer(new RolesTransformer());
}
}
And DataTransformer class:
use AppBundle\Entity\User;
use Symfony\Component\Form\DataTransformerInterface;
class RolesTransformer implements DataTransformerInterface
{
public function transform($roles)
{
foreach ($roles as $role) {
if ($role !== User::ROLE_DEFAULT) {
return $role;
}
}
return $roles[0];
}
public function reverseTransform($string)
{
return [
$string, User::ROLE_DEFAULT
];
}
}
The role attribute from UserModel of FOSUserBundle expected an array. If you use a select normal, they don't return an array.
I'm working on a back office of a restaurant's website. When I add a dish, I can add ingredients in two ways.
In my form template, I manually added a text input field. I applied on this field the autocomplete method of jQuery UI that allows:
Select existing ingredients (previously added)
Add new ingredients
However, when I submit the form, each ingredients are inserted in the database (normal behaviour you will tell me ).
For the ingredients that do not exist it is good, but I don't want to insert again the ingredients already inserted.
Then I thought about Doctrine events, like prePersist(). But I don't see how to proceed. I would like to know if you have any idea of the way to do it.
Here is the buildForm method of my DishType:
<?php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('category', 'entity', array('class' => 'PrototypeAdminBundle:DishCategory',
'property' => 'name',
'multiple' => false ))
->add('title', 'text')
->add('description', 'textarea')
->add('price', 'text')
->add('ingredients', 'collection', array('type' => new IngredientType(),
'allow_add' => true,
'allow_delete' => true,
))
->add('image', new ImageType(), array( 'label' => false ) );
}
and the method in my controller where I handle the form :
<?php
public function addDishAction()
{
$dish = new Dish();
$form = $this->createForm(new DishType, $dish);
$request = $this->get('request');
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($dish);
$em->flush();
return $this->redirect($this->generateUrl('prototype_admin_get_dish', array('slug' => $dish->getSlug())));
}
}
return $this->render('PrototypeAdminBundle:Admin:addDish.html.twig', array(
'form' => $form->createView(),
));
}
I was having the same problem. My entities were projects (dishes in your case) and tags (ingredients).
I solved it by adding an event listener, as explained here.
services:
my.doctrine.listener:
class: Acme\AdminBundle\EventListener\UniqueIngredient
tags:
- { name: doctrine.event_listener, event: preUpdate }
- { name: doctrine.event_listener, event: prePersist }
The listener triggers both prePersist (for newly added dishes) and preUpdate for updates on existing dishes.
The code checks if the ingredient already exists. If the ingredient exists it is used and the new entry is discarded.
The code follows:
<?php
namespace Acme\AdminBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\AdminBundle\Entity\Dish;
use Acme\AdminBundle\Entity\Ingredient;
class UniqueIngredient
{
/**
* This will be called on newly created entities
*/
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
// we're interested in Dishes only
if ($entity instanceof Dish) {
$entityManager = $args->getEntityManager();
$ingredients = $entity->getIngredients();
foreach($ingredients as $key => $ingredient){
// let's check for existance of this ingredient
$results = $entityManager->getRepository('Acme\AdminBundle\Entity\Ingredient')->findBy(array('name' => $ingredient->getName()), array('id' => 'ASC') );
// if ingredient exists use the existing ingredient
if (count($results) > 0){
$ingredients[$key] = $results[0];
}
}
}
}
/**
* Called on updates of existent entities
*
* New ingredients were already created and persisted (although not flushed)
* so we decide now wether to add them to Dishes or delete the duplicated ones
*/
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
// we're interested in Dishes only
if ($entity instanceof Dish) {
$entityManager = $args->getEntityManager();
$ingredients = $entity->getIngredients();
foreach($ingredients as $ingredient){
// let's check for existance of this ingredient
// find by name and sort by id keep the older ingredient first
$results = $entityManager->getRepository('Acme\AdminBundle\Entity\Ingredient')->findBy(array('name' => $ingredient->getName()), array('id' => 'ASC') );
// if ingredient exists at least two rows will be returned
// keep the first and discard the second
if (count($results) > 1){
$knownIngredient = $results[0];
$entity->addIngredient($knownIngredient);
// remove the duplicated ingredient
$duplicatedIngredient = $results[1];
$entityManager->remove($duplicatedIngredient);
}else{
// ingredient doesn't exist yet, add relation
$entity->addIngredient($ingredient);
}
}
}
}
}
NOTE: This seems to be working but I am not a Symfony / Doctrine expert so test your code carefully
Hope this helps!
pcruz
Since this post is 2 years old I don't know if help is still needed here, anyway..
You should place an addIngredient function within your Dish entity, this function checks if the Ingredient object exists within the current ingredient collection.
addIngredient(Ingredient $ingredient){
if (!$this->ingredients->contains($ingredient)) {
$this->ingredients[] = $ingredient;
}
}
Hopefully it could still help you out.