Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Saving Entities

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

Put on your publishing hat, because it's time to write some thoughtful space articles and insert some rows into our article table! And, good news! This is probably one of the easiest things to do in Doctrine.

Let's create a new controller called ArticleAdminController. We'll use this as a place to add new articles. Make it extend the normal AbstractController:

... lines 1 - 2
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
... lines 6 - 8
class ArticleAdminController extends AbstractController
{
... lines 11 - 17
}

And create a public function new():

... lines 1 - 8
class ArticleAdminController extends AbstractController
{
... lines 11 - 13
public function new()
{
... line 16
}
}

Above, add the @Route() - make sure to auto-complete the one from Symfony Components so that PhpStorm adds the use statement. For the URL, how about /admin/article/new:

... lines 1 - 6
use Symfony\Component\Routing\Annotation\Route;
class ArticleAdminController extends AbstractController
{
/**
* @Route("/admin/article/new")
*/
public function new()
{
... line 16
}
}

We're not actually going to build a real page with a form here right now. Instead, I just want to write some code that saves a dummy article to the database.

But first, to make sure I haven't screwed anything up, return a new Response: the one from HttpFoundation with a message:

space rocks... include comets, asteroids & meteoroids

... lines 1 - 5
use Symfony\Component\HttpFoundation\Response;
... lines 7 - 8
class ArticleAdminController extends AbstractController
{
/**
* @Route("/admin/article/new")
*/
public function new()
{
return new Response('space rocks... include comets, asteroids & meteoroids');
}
}

Now, we should be able to find the browser and head to /admin/article/new. Great!

Creating the Article Object

So, here's the big question: how do you save data to the database with Doctrine? The answer... is beautiful: just create an Article object with the data you need, then ask Doctrine to put it into the database.

Start with $article = new Article():

... lines 1 - 4
use App\Entity\Article;
... lines 6 - 9
class ArticleAdminController extends AbstractController
{
... lines 12 - 14
public function new()
{
$article = new Article();
... lines 18 - 45
}
}

For this article's data, go back to the "Why Asteroids Taste like Bacon" article: we'll use this as our dummy news story. Copy the article's title, then call $article->setTitle() and paste:

... lines 1 - 9
class ArticleAdminController extends AbstractController
{
... lines 12 - 14
public function new()
{
$article = new Article();
$article->setTitle('Why Asteroids Taste Like Bacon')
... lines 19 - 45
}
}

This is one of the setter methods that was automatically generated into our entity:

... lines 1 - 9
class Article
{
... lines 12 - 18
/**
* @ORM\Column(type="string", length=255)
*/
private $title;
... lines 23 - 48
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
... lines 55 - 91
}

Oh, and the generator also made all the setter methods return $this, which means you can chain your calls, like: ->setSlug(), then copy the last part of the URL, and paste here. Oh, but we need to make sure this is unique... so just add a little random number at the end:

... lines 1 - 9
class ArticleAdminController extends AbstractController
{
... lines 12 - 14
public function new()
{
$article = new Article();
$article->setTitle('Why Asteroids Taste Like Bacon')
->setSlug('why-asteroids-taste-like-bacon-'.rand(100, 999))
... lines 20 - 45
}
}

Then, ->setContent(). And to get this, go back to ArticleController, copy all of that meaty markdown and paste here. Ah, make sure the content is completely not indented so the multi-line text works:

... lines 1 - 9
class ArticleAdminController extends AbstractController
{
... lines 12 - 14
public function new()
{
$article = new Article();
$article->setTitle('Why Asteroids Taste Like Bacon')
->setSlug('why-asteroids-taste-like-bacon-'.rand(100, 999))
->setContent(<<<EOF
Spicy **jalapeno bacon** ipsum dolor amet veniam shank in dolore. Ham hock nisi landjaeger cow,
lorem proident [beef ribs](https://baconipsum.com/) aute enim veniam ut cillum pork chuck picanha. Dolore reprehenderit
labore minim pork belly spare ribs cupim short loin in. Elit exercitation eiusmod dolore cow
**turkey** shank eu pork belly meatball non cupim.
Laboris beef ribs fatback fugiat eiusmod jowl kielbasa alcatra dolore velit ea ball tip. Pariatur
laboris sunt venison, et laborum dolore minim non meatball. Shankle eu flank aliqua shoulder,
capicola biltong frankfurter boudin cupim officia. Exercitation fugiat consectetur ham. Adipisicing
picanha shank et filet mignon pork belly ut ullamco. Irure velit turducken ground round doner incididunt
occaecat lorem meatball prosciutto quis strip steak.
Meatball adipisicing ribeye bacon strip steak eu. Consectetur ham hock pork hamburger enim strip steak
mollit quis officia meatloaf tri-tip swine. Cow ut reprehenderit, buffalo incididunt in filet mignon
strip steak pork belly aliquip capicola officia. Labore deserunt esse chicken lorem shoulder tail consectetur
cow est ribeye adipisicing. Pig hamburger pork belly enim. Do porchetta minim capicola irure pancetta chuck
fugiat.
EOF
);
... lines 39 - 45
}
}

Much better! The last field is publishedAt. To have more interesting data, let's only publish some articles. So, if a random number between 1 to 10 is greater than 2, publish the article: $article->setPublishedAt() with new \DateTime() and sprintf('-%d days') with a bit more randomness: 1 to 100 days old:

... lines 1 - 9
class ArticleAdminController extends AbstractController
{
... lines 12 - 14
public function new()
{
$article = new Article();
$article->setTitle('Why Asteroids Taste Like Bacon')
->setSlug('why-asteroids-taste-like-bacon-'.rand(100, 999))
->setContent(<<<EOF
Spicy **jalapeno bacon** ipsum dolor amet veniam shank in dolore. Ham hock nisi landjaeger cow,
lorem proident [beef ribs](https://baconipsum.com/) aute enim veniam ut cillum pork chuck picanha. Dolore reprehenderit
labore minim pork belly spare ribs cupim short loin in. Elit exercitation eiusmod dolore cow
**turkey** shank eu pork belly meatball non cupim.
Laboris beef ribs fatback fugiat eiusmod jowl kielbasa alcatra dolore velit ea ball tip. Pariatur
laboris sunt venison, et laborum dolore minim non meatball. Shankle eu flank aliqua shoulder,
capicola biltong frankfurter boudin cupim officia. Exercitation fugiat consectetur ham. Adipisicing
picanha shank et filet mignon pork belly ut ullamco. Irure velit turducken ground round doner incididunt
occaecat lorem meatball prosciutto quis strip steak.
Meatball adipisicing ribeye bacon strip steak eu. Consectetur ham hock pork hamburger enim strip steak
mollit quis officia meatloaf tri-tip swine. Cow ut reprehenderit, buffalo incididunt in filet mignon
strip steak pork belly aliquip capicola officia. Labore deserunt esse chicken lorem shoulder tail consectetur
cow est ribeye adipisicing. Pig hamburger pork belly enim. Do porchetta minim capicola irure pancetta chuck
fugiat.
EOF
);
// publish most articles
if (rand(1, 10) > 2) {
$article->setPublishedAt(new \DateTime(sprintf('-%d days', rand(1, 100))));
}
return new Response('space rocks... include comets, asteroids & meteoroids');
}
}

Perfect! Now... stop. I want you to notice that all we've done is create an Article object and set data on it. This is normal, boring, PHP code: we're not using Doctrine at all yet. That's really cool.

Saving the Article

To save this, we just need to find Doctrine and say:

Hey Doctrine! Say hi to Jon Wage for us! Also, can you please save this article to the database. You're the best!

How do we do this? In the last Symfony tutorial, we talked about how the main thing that a bundle gives us is more services. DoctrineBundle gives us one, very important service that's used for both saving to and fetching from the database. It's called the DeathStar. No, no, it's the EntityManager. But, missed opportunity...

Find your terminal and run:

php bin/console debug:autowiring

Scroll to the the top. There it is! EntityManagerInterface: that's the type-hint we can use to fetch the service. Go back to the top of the new() method and add an argument: EntityManagerInterface $em:

... lines 1 - 5
use Doctrine\ORM\EntityManagerInterface;
... lines 7 - 10
class ArticleAdminController extends AbstractController
{
... lines 13 - 15
public function new(EntityManagerInterface $em)
{
... lines 18 - 53
}
}

Now that we have the all-important entity manager, saving is a two-step process... and it may look a bit weird initially. First, $em->persist($article), then $em->flush():

... lines 1 - 5
use Doctrine\ORM\EntityManagerInterface;
... lines 7 - 10
class ArticleAdminController extends AbstractController
{
... lines 13 - 15
public function new(EntityManagerInterface $em)
{
... lines 18 - 40
// publish most articles
if (rand(1, 10) > 2) {
$article->setPublishedAt(new \DateTime(sprintf('-%d days', rand(1, 100))));
}
$em->persist($article);
$em->flush();
... lines 48 - 53
}
}

It's always these two lines. Persist simply says that you would like to save this article, but Doctrine does not make the INSERT query yet. That happens when you call $em->flush(). Why two separate steps? Well, it gives you a bit more flexibility: you could create ten Article objects, called persist() on each, then flush() just one time at the end. This helps Doctrine optimize saving those ten articles.

At the bottom, let's make our message a bit more helpful, though, I thought my message about space rocks was at least educational. Set the article id to some number and the slug to some string. Pass: $article->getId() and $article->getSlug():

... lines 1 - 5
use Doctrine\ORM\EntityManagerInterface;
... lines 7 - 10
class ArticleAdminController extends AbstractController
{
... lines 13 - 15
public function new(EntityManagerInterface $em)
{
... lines 18 - 45
$em->persist($article);
$em->flush();
return new Response(sprintf(
'Hiya! New Article id: #%d slug: %s',
$article->getId(),
$article->getSlug()
));
}
}

Oh, and this is important: we never set the id. But when we call flush(), Doctrine will insert the new row, get the new id, and put that onto the Article for us. By the time we print this message, the Article will have its new, fancy id.

Ok, are you ready? Let's try it: go back to /admin/article/new and... ha! Article id 1, then 2, 3, 4, 5, 6! Our news site is alive!

If you want to be more sure, you can check this in your favorite database tool like phpMyAdmin or whatever the cool kids are using these days. Or, you can use a helpful console command:

php bin/console doctrine:query:sql "SELECT * FROM article"

This is article with a lowercase "a", because, thanks to the default configuration, Doctrine creates snake case table and column names.

And... yes! There are the new, 6 results.

We have successfully put stuff into the database! Now it's time to run some queries to fetch it back out.

Leave a comment!

36
Login or Register to join the conversation

Once again, you have made Symfony awesome for me, so let me shower some praise. ::shower::

I don't know if this is the right place for this question, but it is regarding Doctrine/MySQL and TimeZones. I was thinking that it would be great for all the DateTime's in MySQL to be in UTC, so that east/west coast programming would not have to deal with EST/EDT. So I googled for an answer for some kind of best practice and did not find a consensus.

I have relied on your Best Practice's and they have made my programming life so much easier (even when you make it more complicated :) ).

So I found these to articles:
https://www.doctrine-projec...
https://blog.bestcoding.net...

What are your thoughts on the subject and is this the best way to implement it. Or can it be "KNPUniversity'ed" up?

1 Reply

Hey Skylar!

Ha! You just made my morning! I feel properly showered in praise - thanks ;).

So..... great question - probably there aren't a lot of answers... because it's complicated... so nobody knows what to say :). First, do you need to track timezones / handle timezones? What I mean is, do you have situations where a user enters a time in their local timezone, and you need to store it in the database? Or, are your dates more internal - e.g. createdAt dates, etc?

We only handle this in a few places in KnpU, but our solution is always to store dates in UTC. In the few places where our users need to enter a time (actually, this only happens in our admin), we make sure to convert the date from the user's timezone into a UTC DateTime before setting it on our entity. Ultimately, everything in the database is in UTC. IF we needed to re-render a date in a local timezone later (we don't have this requirement, but, if we did), we would just need to (A) know which timezone to convert to (which is why you see the timezone sometimes stored in the database - so you know how to convert it *back* later) and then (B) use that to convert the date when rendering it (but keep it in UTC on your entity). Heck, if you had an entity like Event that had a startDateTime field and a timezone field, you could add a new getStartDateInLocalTimezone() method that would create a new DateTime based on the startDateTime UTC and the timezone.

Does that help? Or is there still a missing best-practice piece?

Cheers!

1 Reply

Storing all dates in the database in UTC was my conclusion too. So..... How can we modify the TimeStampableEntity Trait to do that? Or should we implement the conversion as described in the links?

Reply

Hey Skylar

I believe the "TimeStampableEntity" will grab your server's timezone by default, so if you want to always make sure that the date is in UTC, you could do it via a listener, or by creating your own TimeStampableEntity which internally will make that conversion.

Reply
Dirk Avatar
Dirk Avatar Dirk | posted 5 years ago | edited

I've been using Symfony and Doctrine for a few months now, but there's always been a couple of things about Doctrine that made me wonder.
In this example you have added the argument EntityManagerInterface $em to the new() method. Could you tell me why you choose the EntityManagerInterface over the EntityManager?

I usually use $em = $this->getDoctrine()->getManager(); somewhere in the method where i need it. This works, but my IDE tells me that in this case, $em is actually the ObjectManager. Can you explain what the differences are between these three classes (EntityManagerInterface, EntityManager and ObjectManager) and when to use which?

1 Reply

Hey Dirk

Great question!
The ObjectManager interface allows you to work with ORM and ODM, so if for some reason you are using both, or you are planning to switch one to another, then you should type hint your dependencies with "ObjectManager"
The "EntityManagerInterface" contain some specific methods for working with a ORM, like "createQueryBuilder"
and "EntityManager" is just the implementation of "EntityManagerInterface"

No matter what, whenever you can, you should use interfaces for type-hinting your dependencies

Cheers!

1 Reply
Dirk Avatar
Dirk Avatar Dirk | MolloKhan | posted 5 years ago | edited

Hi Diego,

Thanks for taking the time to respond. I'm still somewhat confused between the ObjectManager (which seems to actually be the interface?) and the EntityManagerInterface. From an example on the Symofony website:

`

    // you can fetch the EntityManager via $this->getDoctrine()
    // or you can add an argument to your action: index(EntityManagerInterface $entityManager)
    $entityManager = $this->getDoctrine()->getManager();

`

When I use this method, $entityManager is actually the ObjectManager according to my IDE. When I use: public function new(EntityManagerInterface $entityManager) then $entityManager is the EntityManagerInterface according to my IDE. Is that correct? And if so, does it not really matter in most cases if you're only going to use the ORM? Sorry I ask this again, but it is really confusing to me.

Reply

Yes, you are correct , this line $this->getDoctrine()->getManager(); returns you an instance of ObjectManager interface, which is the main abstraction for woking with ORM and ODM, but as I said, if you are going full ORM, then you don't have to worry about it and type-hint your dependencies with EntityManagerInterface.
Oh, and if you are on Symfony3.3 or higher, then you should be using dependency injection all the time, in other words, do not use $this->getDoctrine() any more :)

Cheers!

Reply
Oliver-W Avatar
Oliver-W Avatar Oliver-W | posted 5 months ago

Hi,
lets say I have a table for storing appointments. A user wants to save 4 appointments, same time and day of the week for the next four weeks.

When I get the first appointment from the form, I can save it without any problem. But,of course, when I change the date of this appointment and flush() it to the database, it changes the date of my first appointment.

So I thought I could make an $appointment2 = new Appointment(); Copy my first appointment on this new one ($appointment2 = $appointment1), change the date on the copy and flush() the copy. But, to my surprise, again my first appointment is changed.

What is the correct way to do this?

Thx
Oliver

Reply

Hey @Oliver-W ,

Keep in mind that the date field is an object :) So, you want to "clone" that object before setting it to the 2nd appointment, otherwise you will set the same object to 2 different entities. And of course, changing the date in one appointment will automatically change the date in the second appointment because that's the same object :)

Basically, just clone the date with $appointment2Date = clone $appointment1Date where the $appointment1Date is a DateTime object from the form. It should do the trick :)

Also, I would recommend you to use datetime_immutable instead of simple datetime for all your dates - it's more "hipster" now :) You can read about the difference in the docs: https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/types.html#datetime-immutable

Cheers!

1 Reply
Meike H. Avatar
Meike H. Avatar Meike H. | posted 3 years ago

Hi, I just started the symfony 4 tutorial doctrine and the database. Creating ArticleAdminController.php and getting the first message in the browser to check the route worked. After finishing that controller with persist and flush I got the error "Attempted to load class "Article" from namespace "App\Controller".
Did you forget a "use" statement for another namespace?" I double checked for typos, but couldnt find any. Can anybody give me a hint about the error message.

Reply

Hey Meike H.

You just need to add the use statement for you Article entity. In other words, add this at the top of your controller (after the namespace)


// App\Controller\YourController.php

use App\Entity\Article;
...

Cheers!

Reply
Meike H. Avatar

Hey Diego,
thanks a lot! It worked. I retyped that section and phpstorm inserted the use-statement automatically. Don't know what went wrong the first time. Maybe I typed the function and didn't use the autocomplete hint.
And thanks to ryan and the symfonycast team for the great tutorials!

1 Reply
Daniel P. Avatar
Daniel P. Avatar Daniel P. | posted 3 years ago

Unlikely that anyone is reading this, but if you are: There's no need to un-indent your Heredoc comments since PHP 7.3! As long as your start and end DOC markers are lined up it should all work fine.

https://laravel-news.com/fl...

And that includes using them within an array argument. How cool is that?

Reply

Cool! Thanks for sharing it.Cheers!

Reply
Ralf B. Avatar
Ralf B. Avatar Ralf B. | posted 3 years ago
Oh, but we need to make sure this is unique... so just add a little random number at the end:

Bad luck. After 7 attempts --> Integrity constraint violation: 1062 Duplicate entry 'why-asteroids-taste-like-bacon311' for key 'UNIQ_23A0E66989D9B62'

Thx for the tutorials, I love them :)

Reply

Ha! wow! That's bad luck indeed. You may want to increase the range of your randomness :)

1 Reply
Galen S. Avatar
Galen S. Avatar Galen S. | posted 4 years ago

Hello, thanks a lot for these great videos. I just have a slight suggestion, and that is perhaps at the end of each video or at the bottom of the script, can all the includes at the top of the page, which includes the namespace declarations and the use declarations be included?

For those of us who aren't using phpstorm, it is quite difficult to keep up with adding those into the file by hand if they aren't shown in the video. Taking the file itself from the finish directory of the included code doesn't always work because sometimes the file is completely different than how it is at the end of an episode.

Most of my mistakes have came from not included something and being confused why I'm getting an error but eventually I figure it out.

Thank you!

Reply

Hey Galen S.!

Thanks for the feedback! I try to be aware of users not using PhpStorm... but even still... yea, we rely on it to do a lot of us (and, even if you're aware of what it's doing, you still can't see the use statements!). I *may* have one suggestion for you - if you haven't noticed it already: all of the code for each chapter is included below each video in code blocks. We only show the "important" parts by default (which should at least *usually* include the use statements, etc) but you can click a button to "expand" and see the whole file. You'd probably still need, for example, 2 tabs open - so you can switch from the video to the code blocks, but I hope it will help. It's one of our solutions for this exact problem.

Let me know!

Cheers!

Reply
Galen S. Avatar

Oh my! You guys are great, and are already on it! I love these videos and this site. I hadn't noticed the "show all lines" button in the top right before, this is exactly what I needed, thank you!

Reply

Woohoo! SO happy this works for you :D. Now, keep going! ;)

Reply
Maxim M. Avatar
Maxim M. Avatar Maxim M. | posted 4 years ago

Interestingly, you can specify the table name with any case in the command:
php bin/console doctrine: query: sql " SELECT * FROM ARTICLE"
php bin/console doctrine: query: sql " SELECT * FROM article"
php bin/console doctrine: query: sql 'SELECT * FROM "ARTIclE"'
And everything works perfectly!

I use PostgreSQL, that's how It behaves if I make queries not through Doctrine, but directly:
https://stackoverflow.com/q...

Probably Doctrine converts the query to lowercase?

Reply

Hey Maxim M.

That's interesting, I believe the `QueryBuilder` for PostgreSQL may have some logic for converting your query string into a proper PostgreSQL query

Cheers!

1 Reply
Md mamunur R. Avatar
Md mamunur R. Avatar Md mamunur R. | posted 5 years ago

Hello, I'm facing this problems,

Attempted to load interface "NotExistingInterface" from namespace
"Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation".
Did you forget a "use" statement for another namespace?

Symfony\Component\Debug\Exception\
ClassNotFoundException

in vendor\symfony\framework-bundle\Tests\Fixtures\Validation\Article.php (line 5)

Reply
JuanLuisGarciaBorrego Avatar
JuanLuisGarciaBorrego Avatar JuanLuisGarciaBorrego | Md mamunur R. | posted 5 years ago

Hey Rashid,

Hm, I only can think that you trying to use this "Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation\Article" class somewhere in your code, see this reference: https://github.com/symfony/...

I suppose you autocompleted a wrong namespace somewhere, try to find the place where you use that namespace. I think you need to replace it with "App\Entity\Article" one. Let me know if you still have this error.

Cheers!

Reply
Md mamunur R. Avatar
Md mamunur R. Avatar Md mamunur R. | JuanLuisGarciaBorrego | posted 5 years ago | edited

Thanks JuanLuisGarciaBorrego ,
Now it's working...

Reply
Michael T. Avatar
Michael T. Avatar Michael T. | posted 5 years ago

Got an error when i hit /admin/article/new at the end of the tutorial:
Type error: Argument 1 passed to App\Entity\Article::setPublishedAt() must be an instance of DateTimeImmutable or null, instance of DateTime given, called in /var/www/phptut/src/Controller/ArticleAdminController.php on line 46
Fixxed it by changing \DateTime to DateTimeImmutable in Line 46: $article->setPublishedAt(new \DateTimeImmutable(sprintf('-%d days', rand(1, 100))));

can u explain what happened here? Cheers

Reply
Dirk Avatar
Dirk Avatar Dirk | Michael T. | posted 5 years ago | edited

Could it be that you have the "type" of "publishedAt" in your Article class set to "datetime_immutable"? So in article controller you see something like:
` /**

 * @ORM\Column(type="datetime_immutable", nullable=true)
 */

private $publishedAt;`

If so, that created the error, because you are setting this attribute to 'datetime_immutable' instead of 'datetime'.

Reply

Thanks Dirk!

Reply
Dirk Avatar

And if that is the case, simply change 'datetime_immutable' to 'datetime'.

Reply
Michael T. Avatar
Michael T. Avatar Michael T. | Dirk | posted 5 years ago

Perfect! Thanks.
i wonder why it set it to datetime_immutable. Must have been some weird autocomplete when i created the entity.

Reply
Dirk Avatar

No problem. Yes, i'm pretty sure it happened with autocompletion, because datetime_immutable is actually a type you could use.

Reply

Just a side note: Windows users should use double quotes on sql command: "SELECT * FROM article"

Reply

Hey Daniel,

Hm, interesting, thanks for sharing it with others. But why not single quotes? Do you see some errors?

Cheers!

Reply
Maxim M. Avatar

On Windows 10 in the standard cmd and in the terminal PhpStorm (which probably works on the basis of cmd):

php bin/console doctrine:query:sql 'SELECT * FROM ARTICLE'
Too many arguments, expected arguments "command" "sql".

In PowerShell or MINGW64 works with any quotes.

Reply

Hey Maxim,

Thanks for the detailed explanation! I actually didn't know that quotes are so important on Windows. Windows console sucks and have weird behavior sometimes :/

Btw, I heard good feedbacks about this console emulator for Windows: https://github.com/cmderdev... . Just in case, maybe it might be useful for other users.

Cheers!

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-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.1.4
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0" // v4.0.14
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "fzaninotto/faker": "^1.7", // v1.7.1
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/maker-bundle": "^1.0", // v1.4.0
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.4
    }
}
userVoice