If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeLet's focus on the edit form first - it'll be a little bit easier to get working. Go to /admin/article
and click to edit one of the existing articles. So, based on the location
, we need to make this specificLocationName
field have different options.
Open ArticleFormType
and go to the bottom. I'm going to paste in a function I wrote called getLocationNameChoices()
. You can copy this function from the code block on this page. But, it's fairly simple: We pass it the $location
string, which will be one of solar_system
, star
or interstellar_space
, and it returns the choices for the specificLocationName
field. If we choose "solar system", it returns planets. If we choose "star", it returns some popular stars. And if we choose "Interstellar space", it returns null
, because we actually don't want the drop-down to be displayed at all in that case.
... lines 1 - 75 | |
private function getLocationNameChoices(string $location) | |
{ | |
$planets = [ | |
'Mercury', | |
'Venus', | |
'Earth', | |
'Mars', | |
'Jupiter', | |
'Saturn', | |
'Uranus', | |
'Neptune', | |
]; | |
$stars = [ | |
'Polaris', | |
'Sirius', | |
'Alpha Centauari A', | |
'Alpha Centauari B', | |
'Betelgeuse', | |
'Rigel', | |
'Other' | |
]; | |
$locationNameChoices = [ | |
'solar_system' => array_combine($planets, $planets), | |
'star' => array_combine($stars, $stars), | |
'interstellar_space' => null, | |
]; | |
return $locationNameChoices[$location]; | |
} | |
... lines 107 - 108 |
Oh, and I'm using array_combine()
just because I want the display values and the values set back on my entity to be the same. This is equivalent to saying 'Mercury' => 'Mercury'
... but saves me some duplication.
The first step to get this working is not so different from something we did earlier. To start, forget about trying to use fancy JavaScript to instantly reload the specificLocationName
drop-down when we select a new location. Yes, we are going to do that - but later.
Hit "Update" the save the location to "The Solar System". The first goal is this: when the form loads, because the location
field is already set, the specificLocationName
should show me the planet list. In other words, we should be able to use the underlying Article
data inside the form to figure out which choices
to use.
I'll add some inline documentation just to tell my editor that this is an Article
object or null
. Then, $location =
, if $article
is an object, then $article->getLocation()
, otherwise, null
.
... lines 1 - 24 | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
/** @var Article|null $article */ | |
$article = $options['data'] ?? null; | |
... line 29 | |
$location = $article ? $article->getLocation() : null; | |
... lines 31 - 65 | |
} | |
... lines 67 - 108 |
Down below, copy the entire specificLocationName
field and remove it. Then only if ($location)
is set, add that field. For choices
, use $this->getLocationNameChoices()
and pass that $location
.
... lines 1 - 24 | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
... lines 27 - 52 | |
if ($location) { | |
$builder->add('specificLocationName', ChoiceType::class, [ | |
'placeholder' => 'Where exactly?', | |
'choices' => $this->getLocationNameChoices($location), | |
'required' => false, | |
]); | |
} | |
... lines 60 - 65 | |
} | |
... lines 67 - 108 |
Cool! Again, no, if we change the location
field, it will not magically update the specificLocationName
field... not yet, at least. With this code, we're saying: when we originally load the form, if there is already a $location
set on our Article
entity, let's add the specificLocationName
field with the correct choices. If there is no location, let's not load that field at all, which means in _form.html.twig
, we need to render this field conditionally: {% if articleForm.specificLocationName is defined %}
, then call form_row()
.
{{ form_start(articleForm) }} | |
... lines 2 - 6 | |
{% if articleForm.specificLocationName is defined %} | |
{{ form_row(articleForm.specificLocationName) }} | |
{% endif %} | |
... lines 10 - 15 | |
{{ form_end(articleForm) }} |
Let's try this! Refresh the page. The Solar System is selected and so... sweet! There is our list of planets! And we can totally save this. Yep! It saved as Earth. Open a second tab and go to the new article form. No surprise: there is no specificLocationName
field here because, of course, the location isn't set yet.
Our system now... sort of works. We can change the data... but we need to do it little-by-little. We can go to "Near a Star", hit "Update" and then change the specificLocationName
field and save that. But I can't do it all at once: I need to fully reload the page... which kinda sucks!
Heck, we can't even be clever! Change location to "The Solar System". Then, inspect element on the next field and change the "Betelgeuse" option to "Earth". In theory, that should work, right? Earth is a valid option when location
is set to solar_system
, and so this should at least be a hacky way to work with the system.
Hit Update. Woh! It does not work! We get a validation error: This value is not valid. Why?
Think about it: when we submit, Symfony first builds the form based on the Article
data that's stored in the database. Because location
is set to star
in the database, it builds the specificLocationName
field with the star options. When it sees earth
being submitted for that field, it looks invalid!
Our form needs to be even smarter: when we submit, the form needs to realize that the location
field changed, and rebuild the specificLocationName
choices before processing the data. Woh.
We can do that by leveraging form events.
Hey Raed,
Difficult to say, better to look at the error stacktrace. I suppose this error is not related to the code you pasted. The problem with that error is that somewhere in your code you're passing a variable to a method that is string instead of "App\Entity\User" object. It would be good to know the exact place, then you would be able to debug that string and see the exact value and figure out why it's so.
Cheers!
Thanks victor for your help ! Its working now, i think this is due a bug in my IDE, i just updated the composer and
it's working, thanks again
How can I save builder EntityType field with multiple selected options to the database (I use mysql 5.7) and when displaying edit form the "select multiple" field to have those options highlighted (with selected attribute)? I load a list of products via 'class' but on after selecting something and hit save i get error:
"<b>Unable to transform value for property path "licensed_products": Expected a Doctrine\Common\Collections\Collection object.</b>"
This is the code from my builder:
`
->add('licensed_products', EntityType::class,[
'class' => Product::class,
'choice_label' => function($choice){
return $choice->getProductName();
},
'multiple' => true
])`
licensed_products is a string currently.
Hey Nikolay S.
Sorry for the slow reply :). So, is the licensed_products property on your entity a relation field (e.g. OneToMany or ManyToMany)? If so, did you remember to initialize the property to an ArrayCollection in the entity's constructor? For example:
class YourEntityClass
{
private $licensed_products;
public function __construct()
{
// check that this line is there!
$this->licensed_products = ArrayCollection();
}
}
Let me know if that's the problem :). If not, if you could share your entity code, that might help!
Cheers!
I have a problem when refresh. The thing is:
When I click edit and the form appear all the saved data is shown. If I edit any field and refresh the page the data i've provided is displayed, but after force refresh the original saved data is displayed from the database. Is this an issue or it's handled by cache?
Hey Nikolay S.
That's odd, makes me thinkg that the data didn't save after edit. Could you try it again but before doing a hard refresh, check the data in the DB and corroborate that it indeed saved?
Cheers!
You didn't understand me right. The data is saving, but if a edit is made on the form but the save button is not clicked, after refresh the new data is shown. If I force refresh the page it shows the original data from the database.
Ahh I get it now and I would say that it's not an expected or common behavior. It does not happen in my browser so it might be your browser's cache our maybe an addon of it
Ah thanks, I had forgot it, so I recreated the entities via make:entity and made the relations and now everything works fine.
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.8.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"sensio/framework-extra-bundle": "^5.1", // v5.2.1
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.1.6
"symfony/console": "^4.0", // v4.1.6
"symfony/flex": "^1.0", // v1.17.6
"symfony/form": "^4.0", // v4.1.6
"symfony/framework-bundle": "^4.0", // v4.1.6
"symfony/orm-pack": "^1.0", // v1.0.6
"symfony/security-bundle": "^4.0", // v4.1.6
"symfony/serializer-pack": "^1.0", // v1.0.1
"symfony/twig-bundle": "^4.0", // v4.1.6
"symfony/validator": "^4.0", // v4.1.6
"symfony/web-server-bundle": "^4.0", // v4.1.6
"symfony/yaml": "^4.0", // v4.1.6
"twig/extensions": "^1.5" // v1.5.2
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.1.6
"symfony/dotenv": "^4.0", // v4.1.6
"symfony/maker-bundle": "^1.0", // v1.8.0
"symfony/monolog-bundle": "^3.0", // v3.3.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.1.6
"symfony/profiler-pack": "^1.0", // v1.0.3
"symfony/var-dumper": "^3.3|^4.0" // v4.1.6
}
}
<blockquote>Expected argument of type "App\Entity\User or null", "string" given.</blockquote>
Hello,
I have this error when i try to update the page with the location, i checked the User entity but i could'not
figure it out ! Any help would be much appreciated !
<b>Here is code for User entity:</b>