I want my AssociationField to display a blank value by defaut, in ordre to force the users to select an item.
As I understand, AssociationField is based on EntityType FormType.
My Problem is, if the field of my entity is required, the create form renders the AssociationField without empty value, and thus selects the first value.
I haven't seen any option to add a placeholder to a select for required properties.
Here are some samples:
My Doctrine entity :
//Player.php
#[ORM\ManyToOne(inversedBy: 'players')]
#[ORM\JoinColumn(name:'team_id', referencedColumnName: 'id', nullable: false)]
private Team $team;
... and my CrudController
//PlayerCrudController.php
public function configureFields(string $pageName): iterable
{
return [
//...
AssociationField::new('team'),
//...
]
}
And the create form displays a select with already a value selected.
I've looked into the docs but couldn't find how to achieve this.
Related
When I use:
public function configureFields(string $pageName): iterable
{
return [
AssociationField::new('XYZ')
];
}`
I get error "Object of class App\Entity\XYZ could not be converted to string"
When I add ->autocomplete() to the AssociationField::new('XYZ'), then it works, but on save it displays error "Expected argument of type "?string", "App\Entity\XYZ" given at property path "XYZ".
Whats the CORRECT way to use this field with many to one relation? Symfony Easy Admin documentation https://symfony.com/doc/current/EasyAdminBundle/fields/AssociationField.html doesn't help at all.
Your entity App\Entity\XYZ will be converted to a string in your association field (which is a standard symfony entity type). Otherwise there is no way to set a label in your entity select.
It will try to convert it using the __toString method, so you need to add it in your entity.
For example:
/**
* #ORM\Entity(repositoryClass=XyzRepository::class)
*/
class Xyz
{
public function __toString(){
return $this->name; //or anything else
}
EasyAdmin should be able to guess the entity class, so you don't have to specify it like you would for a simple EntityType.
I have the following basic test entities:
#Entity()
class Author {
#PrimaryKey()
public id!: number;
#Property()
public name!: string;
}
#Entity()
class Book {
#PrimaryKey()
public id!: number;
#Property()
public title!: string;
#ManyToOne({joinColumn: 'authorID'})
public author!: Author;
}
What i'm trying, is to select only a single Book record, with its 'author', but I care only about its ID, I don't want to actually load the entity.
If I simply call this, it won't work (no author data loaded at all):
em.getRepository(Book).findOne({id: 1}, {fields: ['id', 'title', 'author.id']});
'author.id' doesn't do the trick, the SQL doesn't even contain the 'authorID' field.
If I add 'author' to the fields list as well, it works, author is loaded (only with the ID), but as a separate entity, with a separate, additional SQL statement! That's what I'm trying to avoid.
em.getRepository(Book).findOne({id: 1}, {fields: ['id', 'title', 'author', 'author.id']})
#1. SQL
select `b0`.`id`, `b0`.`title`, `b0`.`authorID` from `book` as `b0` where `b0`.`id` = 1 limit 1
#2. SQL (this wouldn't be neccessary as I want only the ID)
select `a0`.`id` from `author` as `a0` where `a0`.`id` in (2)
--> Result:
Book: { id: 1, title: 'a book', author: { id: 2 } }
The only way I found is to add the specific 'authorID' field too to the Book entity:
#Property()
public authorID!: number;
But, I'd like to avoid introducing these foreign key columns, it would be better to handle through the already existing and used 'author' relation (only by the 'id' property).
Does any solution exists where I could retrieve a relation's ID without generating a 2nd SELECT statement (for the relation), and even avoid introducing the foreign key (next to the already existing relation property)? Would be great to receive through the relation without any extra sql statement.
Thanks in advance.
It is correct behaviour you see the second query, that is how population works, and the fact that you want just a single property from the entity does not change anything, you still populate the relation, and each relation will use its own query to load it. You can use LoadStrategy.JOINED if you want to use a single query. But that would still do a join for that relation, which is not needed for your use case.
Given you only want the FK to be present, you dont need to care about the target entity at all. This should do the trick too:
em.getRepository(Book).findOne(1, {
fields: ['id', 'title', 'author'],
populate: [],
});
This way you say you want those 3 properties to be part of what's selected from the Book entity. You already have the author property, which represents the FK. You will end up with what you want once you serialize such entity. During runtime, you will see entity reference there - an entity with just the PK. It is represented as Ref<Author> when you console.log such entity.
Note that you need that populate: [] there, as otherwise it would be inferred from your fields which contains author property, and that would trigger the full load of it.
I have this entity, if I create a record like this.
$synopsis = new Synopsis();
$synopsis->setPartOne("a");
$synopsis->setPartTwo("b");
$synopsis->setTitle("A");
$synopsis->setSubtitle("B");
$synopsis->setEnabled(false);
$em->persist($synopsis);
$em->flush();
And then I go to my Admin, I see the enabled field to "no" which is expected.
But now, If I use the sonata admin new form field, even if I choose enabled "no", the record is created with enabled = true. And I don't really see why it would be like that.
Here is what I have in my SynopsisAdmin
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('title', TextType::class);
$formMapper->add('subtitle', TextType::class);
$formMapper->add('partOne', TextAreaType::class);
$formMapper->add('partTwo', TextAreaType::class);
$formMapper->add('enabled', BooleanType::class);
}
This is how the enabled field is defined in the entity
/**
* #ORM\Column(type="boolean")
*/
private $enabled;
Thanks for your help.
EDIT: Fun facts too, even if I see no in the sonata view list, when I go to the form view, I see yes instead.
I suspect an error within the sonata core functionnality.
I think you should use the CheckboxType instead of the BooleanType for your Form fields.
Looks like the BooleanType is ment for the list, show and grid actions.
https://symfony.com/doc/master/bundles/SonataAdminBundle/reference/field_types.html
Update
To use the BooleanType you have to set the 'transform' option to true.
This transforms your boolean value to the YES/NO options in the BooleanType:
$formMapper
->add('enabled', BooleanType::class, [
'transform' => true
])
I would like to give formBuilder User Entity as hidden value.
$form->add('user','hidden',array("data" => $user))
$user is User Entity.
However it shows this error.
Expected argument of type "Acme\UserBundle\Entity\User", "string" given
If I use 'null' instead of 'hidden'
$form->add('user',null,array("data" => $user))
it doesn't show the error and shows the select box of user Entity.
However I would like to use hidden.
How can I make it??
You did't specify the field type correctly - this is the correct way:
...
$formBuilder->add('user', HiddenType::class);
...
...
$form = $formBuilder->getForm();
$form->get('user')->setData($user->getId());
But you can't assign entity to the hidden field, so you can assign user's id for user identification.
Another option is to make data transformer and define own EntityHiddenType - more on this here: symfony : can't we have a hidden entity field?
I have started using the Sonata Admin bundle and I was following the example on how to map an entity with the bundle to create an administrative interface.
I created an entity called Post and this is the configuration yml file:
Emiliano\PostsBundle\Entity\Post:
type: entity
table: null
repositoryClass: Emiliano\PostsBundle\Entity\PostRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
title:
type: string
column: Title
lenght: 100
published:
type: boolean
column: Published
publishingDate:
type: datetime
column: Publishing_Date
nullable: TRUE
lifecycleCallbacks: { }
Then in my Admin class I have the configureFormFields method:
protected function configureFormFields(FormMapper $formMapper) {
$formMapper->add('title', 'text')
->add('published', 'checkbox', array('required' => false))
->add('publishingDate', 'sonata_type_model_hidden');
}
I found the sonata_type_model_hidden on the sonata admin documentation. What I would like to achieve is to programmatically handle the publishing date (e.g. set the date only if the checkbox published is checked) hiding the implementation to the user.
Everything works fine for create, delete and read, when it comes to modify an entity I get this message in the stacktrace:
No entity manager defined for class DateTime
In sonata-project/doctrine-orm-admin-bundle/Sonata/DoctrineORMAdminBundle/Model/ModelManager.php at line 214
If I show the field everything works fine, I tried also to use:
->add('publishingDate', 'hidden');
without success.
What is exactly the problem here? Is it because Sonata Admin tries to fill a form with the entity values and for publishingDate there's a DateTime while in the form specification I wrote a sonata_type_model_hidden? If so, how can I circumvent this?
sonata_type_model_hidden isn't just hidden field genereator, according to documentation:
sonata_type_model_hidden will use an instance of ModelHiddenType to render hidden field. The value of hidden field is identifier of related entity.
If I understand your problem, You want to set publishing date only when field published == true
You could use entity preSave/preUpdate lifecycle callback for eaxmple
public function preSave()
{
/**
* Check if item is published
*/
if($this->getPublished()) {
$this->setPublishingDate(new \DateTime());
} else {
$this->setPublishingDate(null);
}
}
and remove publishingDate field from SonataAdmin form.