Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

EntityType: Drop-downs from the Database

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

On submit, we set the author to whoever is currently logged in. I want to change that: sometimes the person who creates the article, isn't the author! They need to be able to select the author.

ChoiceType: Maker of select, radio & checkboxes

Go to the documentation and click back to see the list of form field types. One of the most important types in all of Symfony is the ChoiceType. It's kind of the loud, confident, over-achiever in the group: it's able to create a select drop-down, a multi-select list, radio buttons or checkboxes. It even works on weekends! Phew!

If you think about it, that makes sense: those are all different ways to choose one or more items. You pass this type a choices option - like "Yes" and "No" - and, by default, it will give you a select drop-down. Want radio buttons instead? Brave choice! Just set the expanded option to true. Need to be able to select "multiple" items instead of just one? Totally cool! Set multiple to true to get checkboxes. The ChoiceType is awesome!

But... we have a special case. Yes, we do want a select drop-down, but we want to populate that drop-down from a table in the database. We could use ChoiceType, but a much easier, ah, choice, is EntityType.

Hello EntityType

EntityType is kind of a "sub-type" of choice - you can see that right here: parent type ChoiceType. That means it basically works the same way, but it makes it easy to get the choices from the database and has a few different options.

Head over to ArticleFormType and add the new author field. I'm calling this author because that's the name of the property in the Article class. Well, actually, that doesn't matter. I'm calling this author because this class has setAuthor() and getAuthor() methods: they are what the form system will call behind the scenes.

... lines 1 - 10
class ArticleFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
... lines 16 - 22
->add('author')
... line 24
}
... lines 26 - 32
}

As soon as we add this field, go try it! Refresh! Hello drop-down! It is populated with all the users from the database... but... it might look a little weird. By default, the EntityType queries for all of the User objects and then uses the __toString() method that we have on that class to figure out what display value to use. So, firstName. If we did not have a __toString() method, we would get a huge error because EntityType wouldn't know what to do. Anyways, we'll see in a minute how we can control what's displayed here.

Set the Type, Options are Not Guessed

So... great first step! It looks like the form guessing system correctly sees the Doctrine relation to the User entity and configured the EntityType for us. Go team!

But now, pass the type manually: EntityType::class. That should make no difference, right? After all, the guessing system was already setting that behind the scenes!

... lines 1 - 23
->add('author', EntityType::class)
... lines 25 - 35

Well... we're programmers. And so, we know to expect the unexpected. Try it! Surprise! A huge error!

The required option class is missing

But, why? First, the EntityType has one required option: class. That makes sense: it needs to know which entity to query for. Second, the form type guessing system does more than just guess the form type: it can also guess certain field options. Until now, it was guessing EntityType and the class option!

But, as soon as you pass the field type explicitly, it stops guessing anything. That means that we need to manually set class to User::class. This is why I often omit the 2nd argument if it's being guessed correctly. And, we could do that here.

... lines 1 - 24
->add('author', EntityType::class, [
'class' => User::class,
])
... lines 28 - 38

Try it again. Got it!

Controlling the Option Display Value

Let's go see what else we can do with this field type. Because EntityType's parent is ChoiceType, they share a lot of options. One example is choice_label. If you're not happy with using the __toString() method as the display value for each option... too bad! I mean, you can totally control it with this option!

Add choice_label and set it to email, which means it should call getEmail() on each User object. Try this. I like it! Much more obvious.

... lines 1 - 24
->add('author', EntityType::class, [
... line 26
'choice_label' => 'email',
])
... lines 29 - 39

Want to get fancier? I thought you would. You can also pass this option a callback, which Symfony will call for each item and pass it the data for that option - a User object in this case. Inside, we can return whatever we want. How about return sprintf('(%d) %s') passing $user->getId() and $user->getEmail().

... lines 1 - 24
->add('author', EntityType::class, [
... line 26
'choice_label' => function(User $user) {
return sprintf('(%d) %s', $user->getId(), $user->getEmail());
}
])
... lines 31 - 41

Cool! Refresh that! Got it!

The "Choose an Option" Empty Value

Another useful option that EntityType shares with ChoiceType is placeholder. This is how you can add that "empty" option on top - the one that says something like "Choose your favorite color". It's... a little weird that we don't have this now, and so the first author is auto-selected.

Back on the form, set placeholder to Choose an author. Try that: refresh. Perfecto!

... lines 1 - 24
->add('author', EntityType::class, [
... lines 26 - 29
'placeholder' => 'Choose an author'
])
... lines 32 - 42

With all of this set up, go back to our controller. And... remove that setAuthor() call! Woo! We don't need it anymore because the form will call that method for us and pass the selected User object.

We just learned how to use the EntityType. But... well... we haven't talked about the most important thing that it does for us! Data transforming. Let's talk about that next and learn how to create a custom query to select and order the users in a custom way.

Leave a comment!

45
Login or Register to join the conversation
Yahya E. Avatar
Yahya E. Avatar Yahya E. | posted 3 years ago

One question: I want to extend ChoiceType to MySelect2Type. In MySelect2Type;


public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'expanded'=> false,
'multiple' => false,
'attr' => ['class' => 'select2']
));
...

It works but in Form if I add attr for example to add a data-attribute, it overrides 'attr' => ['class' => 'select2'].

How to overcome this?

22 Reply

Hey Yahya A. Erturan

You almost have it, just add all other attributes into the `attr` array:


$resolver->setDefaults(array(
'attr' => [
'class' => 'select2'
'data-something' => 'some value'
]
));

Cheers!

Reply
Oliver W. Avatar
Oliver W. Avatar Oliver W. | posted 1 year ago

Hello,
I am experimenting with the code and have made my own Form, Repository and Controller. For adding new articles, everything works fine. But when I try to edit an article, I am getting 'Entity of type "Doctrine\ORM\PersistentColletion" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?'.
This seems to be rather clear, but nevertheless I am stuck. Any idea why or is additional information needed?
Thx
Oliver

Reply

Hey Oliver Wagner

That's interesting, I think you forgot to assign a field somewhere in your Article entity. What it's basically saying is that you forgot to call persist on a new entity object but when you edit an entity you don't need to call persist on it again (that's the odd part). I think I'd need to see your code to spot the error

Reply
Oliver W. Avatar

Hi Diego Aguiar,

thx for your hint concerning the droping.

But I had no luck with my problem. My Articles.php:

images = new ArrayCollection();
$this->category = new ArrayCollection();
}

public function getId(): ?int
{
return $this->id;
}

public function getName(): ?string
{
return $this->name;
}

public function setName(string $name): self
{
$this->name = $name;

return $this;
}

public function getBoughtAt(): ?\DateTimeInterface
{
return $this->boughtAt;
}

public function setBoughtAt(\DateTimeInterface $boughtAt): self
{
$this->boughtAt = $boughtAt;

return $this;
}

public function getWarehouse(): ?Warehouses
{
return $this->warehouse;
}

public function setWarehouse(?Warehouses $warehouse): self
{
$this->warehouse = $warehouse;

return $this;
}

public function getLocation(): ?string
{
return $this->location;
}

public function setLocation(string $location): self
{
$this->location = $location;

return $this;
}

public function getOnstock(): ?int
{
return $this->onstock;
}

public function setOnstock(int $onstock): self
{
$this->onstock = $onstock;

return $this;
}

public function getPrimecost(): ?float
{
return $this->primecost;
}

public function setPrimecost(float $primecost): self
{
$this->primecost = $primecost;

return $this;
}

public function getRetail(): ?float
{
return $this->retail;
}

public function setRetail(float $retail): self
{
$this->retail = $retail;

return $this;
}

/**
* @return Collection|Images[]
*/
public function getImages(): Collection
{
return $this->images;
}

public function addImage(Images $image): self
{
if (!$this->images->contains($image)) {
$this->images[] = $image;
$image->setArticle($this);
}

return $this;
}

public function removeImage(Images $image): self
{
if ($this->images->contains($image)) {
$this->images->removeElement($image);
// set the owning side to null (unless already changed)
if ($image->getArticle() === $this) {
$image->setArticle(null);
}
}

return $this;
}

public function getComment(): ?string
{
return $this->comment;
}

public function setComment(?string $comment): self
{
$this->comment = $comment;

return $this;
}

/**
* @return Collection|Descriptions[]
*/
public function getCategory(): Collection
{
return $this->category;
}

public function addCategory(Descriptions $category): self
{
if (!$this->category->contains($category)) {
$this->category[] = $category;
}

return $this;
}

public function removeCategory(Descriptions $category): self
{
if ($this->category->contains($category)) {
$this->category->removeElement($category);
}

return $this;
}
}

Do you need anything else? And thx for your assistance.
Oliver

Reply

Hey Oliver Wagner
Hmm, I can't spot the error. Could I see the full constructor code? It got cut, also the FormType code, please.

Reply
Oliver W. Avatar

Hi Diego Aguiar,
I repeatedly tried to post the whole code, but there are ALWAYS several lines at the beginning, that are cut off!?!?!? This happens for every listing. For the FormType too. When I click von Edit, the lines are shown. I've got the impression, everything until the constructor is omitted. May be a bug in this application?
Are there other ways to get the code to you? Find it here: http://cloud.ad-libitum.inf.... Valid until next monday.
I found that the problem is related to the ordering of my categories alphabetically in ArticleFormType. If I only use "->add('category')" it works fine. I also noticed that my new-form is displayes, but I get an error when trying to save: The property "category" in class "App\Entity\Articles" can be defined with the methods "addCategory()=", "removeCategory()" but the new value must be an array or an instance of \Traversable. Futher more it is NOT possible to select more than one category. So probably there is missing some array thing.
HTH and thx
Oliver

Reply

Hey Oliver Wagner
I'm not sure if I'm missing something but your code looks good to me. I'd need to play a bit with your application to spot the problem. But, there's something you can try before that. It's likely that you're not assigning the other side of the relationship. In your Descriptions entity, double-check that the addArticle() method sets both sides (I couldn't check that because you didn't upload that entity :p)
BTW, this video may help you out understanding a bit better how to manage ManyToMany relationships https://symfonycasts.com/sc...

If you still have problems, upload your project to Github or wherever you want and give me access so I can play with it. Cheers!

Reply
Oliver W. Avatar

Hi Diego Aguiar

find the app at the known place. I still couldn't manage to chose more than one Category during the creation of new articles (Artikel). And editing from the list of articles throws the error.
Have a nice weekend

Reply

Hey Oliver Wagner
I finally discovered what's the problem. We forgot to make the category field a multi-select! You only have to pass 'multiple' => true into the options array of the field. It's a detail to keep in mind when working with ManyToMany relationships
https://symfony.com/doc/cur...

I felt a bit dump when I realized that... Oh programming, you always surprise me :D

Reply
Oliver W. Avatar

Hi Diego Aguiar
you're the greatest. That was the missing link for both problems.
Thx so lot and have a nice week.
Oliver

1 Reply
Oliver W. Avatar

Hi Diego Aguiar,
II found a probaly suspicious. But I can't figure out how to remove a field from a database. Adding is done via php bin/console make:entity. But how can I remove a field from a existing database and update my migrations at the same time?
Thx
Oliver

Reply

If I understood you correctly, what you want is to only remove a field from a database table which is not mapped to an entity? Because if it's mapped, then you can just remove the property on the entity and generate a migration. What you can do is to create manually a migration, just copy-paste an old migration, tweak its name and write the SQL you need to *drop* a field from the table you want. That should do the trick
Cheers!

Reply
Oliver W. Avatar
Oliver W. Avatar Oliver W. | posted 2 years ago

I've got the feeling, I missed something. How does Symfony know that it should use the field user.first_name to populate the dropdown to fill article.author using ->add('author')??? I understand the target, but not the source!

Reply

Hey Oliver Wagner

That's because we have the __toString() method on the User class, and that method calls and returns getFirstname() method. You can rely on that method or define the choice_label option as shown in the video

Cheers!

Reply
Oliver W. Avatar

Thx Diego.

I've made one in tag.php too and still get the users. So there has to be more!? May be a setting from Mastering Doctrine Relations! that I haven't watched yet.
With choice_label it is the same. Why uses Symfony the table users?

Reply

> Why uses Symfony the table users?
Because the author field is related to the User class, when you add a field to your form this way $builder->add('author'); Symfony uses an interfal "guesser" feature that reads the metadata of such field. Then, if you don't specify a choice label option, it will invoke the __toString() method of the object.
In this case, the user class has implemented the __toString() method, and it returns the first name

Does it makes more sense now?
Cheers!

Reply
Oliver W. Avatar

Thx Diego. I'm afraid we were talking at cross-purposes.
Meanwhile I've been looking at the Desginer in phpMyAdmin and found that there are relations between the tables of the database. One is from article.author_id to user.id. Thats exactly the missing link. I suppose these relations have been set in the course " Mastering Doctrine Relations". Till now I did not make this one. I guess I should catch up ;-)

That's the explanation why my __toString() in the table tag has no effect on the "guessing" of Symfony. Since there is no relation between article.author_id and tag.name.

Reply

Ohh I get it now. Sorry for the confusion. Yes, the relationship with the user class is already set up at this point of the tutorial. I can't remember exactly in what chapter we add the relationship but if you download the course code from this page you can see what to do, or watch the "Mastering Doctrine Relations" course to dive deep into relationships

If you still have doubts, pleaes let me know.

Reply

Hello!

I got a performance issue when trying a more "sophisticated" choice_label.

Here is my code:

$builder->add('postalCode', EntityType::class, [
'class' => PostalCode::class,
'choice_label' => function(PostalCode $postalCode) {
return sprintf('%d (%s)', $postalCode->getPostalCode(), $postalCode->getCity());
},
'label' => $this->translator->trans('form.postalCode.label',[], 'users'),
'placeholder' => $this->translator->trans('form.postalCode.placeholder', [], 'users'),
])

The PostalCode entity is a translatable entity and the city is one of the translated field.


use Knp\DoctrineBehaviors\Model\Translatable\Translatable;

class PostalCode {
use Translatable;
...
}


use Knp\DoctrineBehaviors\Model\Translatable\Translation;

class PostalCodeTranslation
{
use Translation;
...
}

If I just print the postalCode field, the performance is good.
Any idea?
Thx!

Reply

Hey Lydie,

I'd recommend you to look at Symfony's web debug profile. What do you mean about performance hit? Most probably you execute a lot of queries to the DB, but would be good to know this for sure like how much queries are executed behind the scene when you create this form, that's where Symfony profiler could help.

I suppose that "$postalCode->getCity()" query for related entity for each postal code, so if you have hundreds of them - that might be the problem. If it's the exact reason - it would be good if you can pre-load all those translatable entities, I think you can do it with "query_builder" option: https://symfony.com/doc/cur... - something like this:


$builder->add('postalCode', EntityType::class, [
'class' => postalCode::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('postalCode')
->select('postalCode')
->addSelect('translations')
->innerJoin('postalCode.translations', 'translations');
},
// ...
]);

But it still might be issue with memory. Anyway, it's important to understand the exact reason of performance degradation. Otherwise, maybe look for a way to translate that field with Symfony Translation component instead?

I hope this helps!

Cheers!

Reply

Thx Victor! This was indeed the issue (N+1 issue if I am not wrong). I had 2782 queries (so it took time ...) and after doing your suggested modification, the number decreased to 17. Good progress, isn't it ? :) I will check the other queries I made on this site to see if I can improve some others too.

Thx again!!

Reply

Hey Lydie,

Thanks for your feedback! Sounds like a great optimization ;) I'm glad it helped you!

Cheers!

Reply
Bhagwandas P. Avatar
Bhagwandas P. Avatar Bhagwandas P. | posted 3 years ago

Trying to create a form which has one unmapped choices field and dynamic values.

public function buildForm(FormBuilderInterface $project, array $options)
{

$project->add('users', EntityType::class, [
'choice_label' => 'name',
'label' => 'Select Users',
'expanded' => true,
'multiple' => true,
'class' => 'App:User',
'mapped' => false,
'choices' => $options['users'],
]);
}

List of choices comes from an array of User type objects which is passed in options while trying to create form.

$form = $this->createForm(ProjectFormType::class, $project, array('users' => $users));

How do I show already selected Users whlie trying to edit the form? It takes automatically, if it is mapped field.
Also, is there a better way to pass users data for choices?

Reply

Hey Rahul Bhargava

> How do I show already selected Users whlie trying to edit the form?
The second argument you pass to $this->createForm() represent the initial data of the form, so in this case you should pass an array with a "users" key, or even better if you create a DataModel class and bind it to the form.

> is there a better way to pass users data for choices?
I don't see anything wrong on how you are currently doing it

Cheers!

Reply
Mike P. Avatar
Mike P. Avatar Mike P. | posted 3 years ago

How can SF handle new dynamic form fields?
By example, I have a "new recipe" form with ingredients.
Name of ingredient, amount of ingredient, type of amount (g, mg, etc.)

Now I have a "+" button, to add more of these 3 fields per ingredient via JS.
How can I let SF handle this type of the "same" field multiple times?
Any tips what/how I should use SF forms?

Thanks in advance, your sf tutorials are superb :)

Reply

Hey Mike

There is a feature in Symfony forms called "propotype" that allows you to do specifically that. Here is a video where Ryan implements it: https://symfonycasts.com/sc...
or you can check the docs: https://symfony.com/doc/cur...

Cheers!

Reply
Vesna Avatar

Hi,

One question: if I have 2 dropdowns on my form, for example:

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('region', EntityType::class, [
'class' => Products::class,
'choice_label' => 'region'
])
->add('city', EntityType::class, [
'class' => Products::class,
'choice_label' => 'city'
])
;
}

than __toString() returns just one value
for example:

public function __toString() {
return $this->city;
}

and saves in db city name that was chosen.

But how to save both values.
City and Region?

Reply

Hey Vex

Is there any particular reason on using the EntityType for such fields? What you can do is to create a ProductFormType and just add those fields as ChoiceType or whatever other type that fit your needs

Cheers!

Reply
Vesna Avatar

Hi @Diego

Thanks for quick reply.
I'm still figuring out how this works. :)
I will try with CustomFormType.

Thanks!!!

Reply
Vesna Avatar

Diego Aguiar
One more question.

How to solve this error "You cannot add children to a simple form. Maybe you should set the option "compound" to true?"
I'm using easyadminbundle, and trying to add ProductFormType, but constantly getting this error :(

Reply

Hmm, how are you telling EasyAdmin to use your form type? Also, let me see your CustomFormType code

Reply
Vesna Avatar

Hi Diego Aguiar

in easy_admin.yaml:


form:
fields:
- { property: 'name', css_class: 'large', label: 'Product Name' }
- { property: 'product', type: 'App\Form\ProductFormType', columns: 4 }


and in CustomFormType:


class ProductsFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('product', EntityType::class, array(
'class' => Product::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('p')
->orderBy('p.city', 'ASC');
},
));

}

public function getParent()
{
return ChoiceType::class;
}

}


Am I missing something? :(

Reply

I think you only have to remove the "getParent" method, but you can achieve the same result with almost pure config. What you want is to specify the "query_builder" option, and you can do it like so:


form:
fields:
- property: 'product'
- type_options: { query_builder: 'App\Repository\ProductRepository::CustomRepoMethod'}

Cheers!

Reply

I have an issue while trying to save the user role(s) to the database. I get the famous Expected argument of type "array", "string" given at property path "roles". The issue occurs when I actually hit the save button. The initial form rendering works as expected.

While setting the 'multiple' => true, as many suggest, this is not the way I need my option to be set. I just want to be able to pick an option from the dropdown.

I thought of creating an Entity for the roles but this would not make sense since S4 has all the basic roles in the core already.

My code looks like this:

the FormType: OrganizationUserType.php


->add('roles', ChoiceType::class, [
'label' => 'Role',
'placeholder' => 'Choose an option',
'required' => 'false',
'choices' => [
'Member' => 'ROLE_USER',
'Admin' => 'ROLE_ADMIN',
]
])

the Entity: OrganizationUser.php


/**
* @ORM\Column(type="json")
*/
private $roles = [];

/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';

return array_unique($roles);
}

public function setRoles(array $roles): self
{
$this->roles = $roles;

return $this;
}

Any thoughts on how could I handle this one?

Cheers!

Reply

Hey Radu Barbu

So, the case is that you are passing a String when what you really want is to pass an Array, in this case what you need is to apply a DataTransformer into the desired field.
You can see how Ryan implements his own DataTransformer here: https://symfonycasts.com/sc...

Cheers!

Reply
Default user avatar

Return value of App\Entity\Article::getAuthor() must be an instance of App\Entity\User, null returned

Reply

Hey Roman!

Interesting! This tells me that the Form system is calling getAuthor() on your User object... which is odd. Are you passing an Article object when you call createForm()?

Cheers!

Reply
Dirk Avatar

This is very nice, thank you. I am using the 'choice_label' now to combine two attributes of an entity I want to have in that label. I wonder (and I don't know if this is even possible or has anything to do with symfony forms in particular), would it be possible to have the names of both attributes in the label, but only have one of them visible? You might wonder why I would want such a thing, but this is because I also use Select2 to search for the options, and I would like to be able to search for my second attribute, but not show it. Thx!

Reply

Hey Dirk J. Faber

That's a nice question, you made me wonder about it for a moment :)

What I would do if I were on your case, I would add the exact text that you want to use for searching in a data attribute by defining the "choice_attr" field (https://symfony.com/doc/cur..., then, you can change the logic of how "Select2" searches for things (https://select2.org/searching). I don't have a real example but I hope it gives you an idea of how to achieve your goal :)

Cheers!

Reply
Dirk Avatar

Hi Diego,

Thank you once again for your help. I am gonna give it a shot with your suggestion, and if I manage to succeed, of course I will let you know!

1 Reply
Dirk Avatar

It took me a while, but I managed to make it work, thanks to your suggestion. The reason it took me so long is because in all honesty I don't really understand JavaScript.
I have this Entity called 'Institution' that has the attribute 'internationalName' (used in the __toString() ) and 'abbreviation'. I wanted users to be able to search not only on the name, but also the abbreviation. So to the IntitutionType i added:

'choice_attr' => function(Institution $institution) {
// adds a class 'data-abbreviation'.
return ['data-abbreviation' => $institution->getAbbreviation()];
}

And then some JS:


function abbreviationMatcher(params, data) {

// If there are no search terms, return all of the data
if ($.trim(params.term) === '') {
return data;
}

// Do not display the item if there is no 'text' property
if (typeof data.text === 'undefined') {
return null;
}

// Check if the international name occurs
if (data.text.toLowerCase().indexOf(params.term.toLowerCase()) > -1){
return data;
}

// Check if the data of second attribute 'abbreviation' occurs
if ($(data.element).data('abbreviation').toLowerCase().indexOf(params.term.toLowerCase()) > -1 ) {
return data;
}

// Return `null` if the term should not be displayed
return null;
}

$('select[data-select="true"]').each(function () {

$(this).select2(
{ matcher: abbreviationMatcher, width: '100%' , placeholder: $(this).data('placeholder') || $(this).attr('data-placeholder' )}

)
});

The only thing I cannot get working is to strip the diacritics, like Select2 does.

Reply

Hey Dirk J. Faber!

Sorry for the late reply. I've been sick (I damn you flu!), about your problem, I believe that's a problem related to the CHAR set. Have you tried to apply a UTF8 encoding?

Cheers!

Reply
Dirk Avatar

I hope you are feeling better! I don't think the CHAR set is the problem, because with the regular matching code everything works fine. This is (I think) because of a method called 'stripDiacritics'. The problem I have is that when I use a custom matcher (to find also the abbreviation) I cannot seem to access this method stripDiacritic.

Reply

Hmm, I think that's because of the custom matcher function scope level (woh, that was long), it might be at another level where it cannot access to the "stripDiacritic" function. To be honest I'm not sure how to properly fix it but probably you are able to replicate that function and load it at a scope level where you can actually use it.

Cheers and sorry for the late replay (again!).

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// 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
    }
}